From efb1564f940f67fe86f998dcdc7865fdd390fb5d Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 18 Oct 2024 09:44:09 +0200 Subject: [PATCH] Add globalHubMode option --- .../jakarta/SentryAutoConfigurationTest.kt | 2 ++ .../boot/SentryAutoConfigurationTest.kt | 2 ++ sentry/api/sentry.api | 4 +++ .../main/java/io/sentry/ExternalOptions.java | 12 +++++++ sentry/src/main/java/io/sentry/Sentry.java | 7 ++-- .../main/java/io/sentry/SentryOptions.java | 27 +++++++++++++++ .../java/io/sentry/ExternalOptionsTest.kt | 14 ++++++++ .../test/java/io/sentry/SentryOptionsTest.kt | 7 ++++ sentry/src/test/java/io/sentry/SentryTest.kt | 34 +++++++++++++++++++ 9 files changed, 106 insertions(+), 3 deletions(-) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt index 649b536442..af2bb4335b 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt @@ -174,6 +174,7 @@ class SentryAutoConfigurationTest { "sentry.enable-spotlight=true", "sentry.spotlight-connection-url=http://local.sentry.io:1234", "sentry.force-init=true", + "sentry.global-hub-mode=true", "sentry.cron.default-checkin-margin=10", "sentry.cron.default-max-runtime=30", "sentry.cron.default-timezone=America/New_York", @@ -211,6 +212,7 @@ class SentryAutoConfigurationTest { assertThat(options.ignoredCheckIns).containsOnly("slug1", "slugB") assertThat(options.isEnableBackpressureHandling).isEqualTo(false) assertThat(options.isForceInit).isEqualTo(true) + assertThat(options.isGlobalHubMode).isEqualTo(true) assertThat(options.isEnableSpotlight).isEqualTo(true) assertThat(options.spotlightConnectionUrl).isEqualTo("http://local.sentry.io:1234") assertThat(options.cron).isNotNull diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt index 173a8586f8..2cb711d350 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt @@ -173,6 +173,7 @@ class SentryAutoConfigurationTest { "sentry.enable-spotlight=true", "sentry.spotlight-connection-url=http://local.sentry.io:1234", "sentry.force-init=true", + "sentry.global-hub-mode=true", "sentry.cron.default-checkin-margin=10", "sentry.cron.default-max-runtime=30", "sentry.cron.default-timezone=America/New_York", @@ -210,6 +211,7 @@ class SentryAutoConfigurationTest { assertThat(options.ignoredCheckIns).containsOnly("slug1", "slugB") assertThat(options.isEnableBackpressureHandling).isEqualTo(false) assertThat(options.isForceInit).isEqualTo(true) + assertThat(options.isGlobalHubMode).isEqualTo(true) assertThat(options.isEnableSpotlight).isEqualTo(true) assertThat(options.spotlightConnectionUrl).isEqualTo("http://local.sentry.io:1234") assertThat(options.cron).isNotNull diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 7ccc63a19e..fbd8d91d0d 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -473,6 +473,7 @@ public final class io/sentry/ExternalOptions { public fun isEnableSpotlight ()Ljava/lang/Boolean; public fun isEnabled ()Ljava/lang/Boolean; public fun isForceInit ()Ljava/lang/Boolean; + public fun isGlobalHubMode ()Ljava/lang/Boolean; public fun isSendDefaultPii ()Ljava/lang/Boolean; public fun isSendModules ()Ljava/lang/Boolean; public fun setCron (Lio/sentry/SentryOptions$Cron;)V @@ -487,6 +488,7 @@ public final class io/sentry/ExternalOptions { public fun setEnabled (Ljava/lang/Boolean;)V public fun setEnvironment (Ljava/lang/String;)V public fun setForceInit (Ljava/lang/Boolean;)V + public fun setGlobalHubMode (Ljava/lang/Boolean;)V public fun setIdleTimeout (Ljava/lang/Long;)V public fun setIgnoredCheckIns (Ljava/util/List;)V public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V @@ -2891,6 +2893,7 @@ public class io/sentry/SentryOptions { public fun isEnableUserInteractionTracing ()Z public fun isEnabled ()Z public fun isForceInit ()Z + public fun isGlobalHubMode ()Ljava/lang/Boolean; public fun isPrintUncaughtStackTrace ()Z public fun isProfilingEnabled ()Z public fun isSendClientReports ()Z @@ -2943,6 +2946,7 @@ public class io/sentry/SentryOptions { public fun setForceInit (Z)V public fun setFullyDisplayedReporter (Lio/sentry/FullyDisplayedReporter;)V public fun setGestureTargetLocators (Ljava/util/List;)V + public fun setGlobalHubMode (Ljava/lang/Boolean;)V public fun setIdleTimeout (Ljava/lang/Long;)V public fun setIgnoredCheckIns (Ljava/util/List;)V public fun setIgnoredSpanOrigins (Ljava/util/List;)V diff --git a/sentry/src/main/java/io/sentry/ExternalOptions.java b/sentry/src/main/java/io/sentry/ExternalOptions.java index 75b67dc766..50d313cff3 100644 --- a/sentry/src/main/java/io/sentry/ExternalOptions.java +++ b/sentry/src/main/java/io/sentry/ExternalOptions.java @@ -52,6 +52,7 @@ public final class ExternalOptions { private @Nullable Boolean sendModules; private @Nullable Boolean sendDefaultPii; private @Nullable Boolean enableBackpressureHandling; + private @Nullable Boolean globalHubMode; private @Nullable Boolean forceInit; private @Nullable SentryOptions.Cron cron; @@ -141,6 +142,8 @@ public final class ExternalOptions { options.setEnableBackpressureHandling( propertiesProvider.getBooleanProperty("enable-backpressure-handling")); + options.setGlobalHubMode(propertiesProvider.getBooleanProperty("global-hub-mode")); + for (final String ignoredExceptionType : propertiesProvider.getList("ignored-exceptions-for-type")) { try { @@ -437,6 +440,15 @@ public void setEnableBackpressureHandling(final @Nullable Boolean enableBackpres return enableBackpressureHandling; } + public void setGlobalHubMode(final @Nullable Boolean globalHubMode) { + this.globalHubMode = globalHubMode; + } + + @ApiStatus.Experimental + public @Nullable Boolean isGlobalHubMode() { + return globalHubMode; + } + public void setForceInit(final @Nullable Boolean forceInit) { this.forceInit = forceInit; } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index fbd05e15e3..d27946eb3e 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -267,7 +267,6 @@ public static void init(final @NotNull SentryOptions options) { @SuppressWarnings("deprecation") private static void init(final @NotNull SentryOptions options, final boolean globalHubMode) { try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { - if (!options.getClass().getName().equals("io.sentry.android.core.SentryAndroidOptions") && Platform.isAndroid()) { throw new IllegalArgumentException( @@ -279,10 +278,12 @@ private static void init(final @NotNull SentryOptions options, final boolean glo return; } + final boolean globalHubModeToUse = + options.isGlobalHubMode() != null ? options.isGlobalHubMode() : globalHubMode; options .getLogger() - .log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); - Sentry.globalHubMode = globalHubMode; + .log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubModeToUse)); + Sentry.globalHubMode = globalHubModeToUse; final boolean shouldInit = InitUtil.shouldInit(globalScope.getOptions(), options, isEnabled()); if (shouldInit) { diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 4855712742..e4c38abaf2 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -496,6 +496,9 @@ public class SentryOptions { private boolean forceInit = false; + // TODO replace hub in name + private @Nullable Boolean globalHubMode = null; + protected final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock(); /** @@ -2370,6 +2373,26 @@ public boolean isForceInit() { return forceInit; } + /** + * If set to true, automatic scope forking will be disabled. If set to false, scopes will be + * forked automatically, e.g. when scopes are accessed on a thread for the first time, pushScope + * is invoked, in some cases when we explicitly want to fork the root scopes, etc. + * + *

If this is set to something other than `null`, it will take precedence over what is passed + * to Sentry.init. + * + *

Enabling this is intended for mobile and desktop apps, not backends. + * + * @param globalHubMode true = automatic scope forking is disabled + */ + public void setGlobalHubMode(final @Nullable Boolean globalHubMode) { + this.globalHubMode = globalHubMode; + } + + public @Nullable Boolean isGlobalHubMode() { + return globalHubMode; + } + /** The BeforeSend callback */ public interface BeforeSendCallback { @@ -2634,6 +2657,10 @@ public void merge(final @NotNull ExternalOptions options) { setSpotlightConnectionUrl(options.getSpotlightConnectionUrl()); } + if (options.isGlobalHubMode() != null) { + setGlobalHubMode(options.isGlobalHubMode()); + } + if (options.getCron() != null) { if (getCron() == null) { setCron(options.getCron()); diff --git a/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt index c61f789da9..5507c5aa14 100644 --- a/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt @@ -307,6 +307,20 @@ class ExternalOptionsTest { } } + @Test + fun `creates options with globalHubMode set to true`() { + withPropertiesFile("global-hub-mode=true") { options -> + assertTrue(options.isGlobalHubMode == true) + } + } + + @Test + fun `creates options with globalHubMode set to false`() { + withPropertiesFile("global-hub-mode=false") { options -> + assertTrue(options.isGlobalHubMode == false) + } + } + private fun withPropertiesFile(textLines: List = emptyList(), logger: ILogger = mock(), fn: (ExternalOptions) -> Unit) { // create a sentry.properties file in temporary folder val temporaryFolder = TemporaryFolder() diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index a7d11e6660..21f33383e7 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -338,6 +338,7 @@ class SentryOptionsTest { } externalOptions.isEnableSpotlight = true externalOptions.spotlightConnectionUrl = "http://local.sentry.io:1234" + externalOptions.isGlobalHubMode = true val options = SentryOptions() @@ -379,6 +380,7 @@ class SentryOptionsTest { assertEquals(RequestSize.MEDIUM, options.maxRequestBodySize) assertTrue(options.isEnableSpotlight) assertEquals("http://local.sentry.io:1234", options.spotlightConnectionUrl) + assertTrue(options.isGlobalHubMode!!) } @Test @@ -547,6 +549,11 @@ class SentryOptionsTest { assertFalse(SentryOptions().isEnableAppStartProfiling) } + @Test + fun `when options are initialized, isGlobalHubMode is set to null by default`() { + assertNull(SentryOptions().isGlobalHubMode) + } + @Test fun `when setEnableAppStartProfiling is called, overrides default`() { val options = SentryOptions() diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 09b61399cd..5c707a9893 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -49,7 +49,9 @@ import kotlin.test.assertFalse import kotlin.test.assertIs import kotlin.test.assertNotEquals import kotlin.test.assertNotNull +import kotlin.test.assertNotSame import kotlin.test.assertNull +import kotlin.test.assertSame import kotlin.test.assertTrue class SentryTest { @@ -1243,6 +1245,38 @@ class SentryTest { } } + @Test + fun `if globalHubMode on options is not set, uses false from init param`() { + Sentry.init({ o -> o.dsn = dsn }, false) + val s1 = Sentry.forkedRootScopes("s1") + val s2 = Sentry.forkedRootScopes("s2") + assertNotSame(s1, s2) + } + + @Test + fun `if globalHubMode on options is not set, uses true from init param`() { + Sentry.init({ o -> o.dsn = dsn }, true) + val s1 = Sentry.forkedRootScopes("s1") + val s2 = Sentry.forkedRootScopes("s2") + assertSame(s1, s2) + } + + @Test + fun `if globalHubMode on options is set, ignores false from init param`() { + Sentry.init({ o -> o.dsn = dsn; o.isGlobalHubMode = true }, false) + val s1 = Sentry.forkedRootScopes("s1") + val s2 = Sentry.forkedRootScopes("s2") + assertSame(s1, s2) + } + + @Test + fun `if globalHubMode on options is set, ignores true from init param`() { + Sentry.init({ o -> o.dsn = dsn; o.isGlobalHubMode = false }, true) + val s1 = Sentry.forkedRootScopes("s1") + val s2 = Sentry.forkedRootScopes("s2") + assertNotSame(s1, s2) + } + private class InMemoryOptionsObserver : IOptionsObserver { var release: String? = null private set