diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index 53021996706..eac29bc42db 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -41,8 +41,11 @@ public final class InternalSentrySdk { @Nullable public static Scope getCurrentScope() { final @NotNull AtomicReference scopeRef = new AtomicReference<>(); - //noinspection Convert2MethodRef - HubAdapter.getInstance().withScope(scope -> scopeRef.set(scope)); + HubAdapter.getInstance() + .configureScope( + scope -> { + scopeRef.set(new Scope(scope)); + }); return scopeRef.get(); } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index c27e199d6c6..f10594cf0b7 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -119,6 +119,27 @@ class InternalSentrySdkTest { assertNotNull(scope) } + @Test + fun `current scope returns a copy of the scope`() { + Sentry.setCurrentHub( + Hub( + SentryOptions().apply { + dsn = "https://key@uri/1234567" + } + ) + ) + Sentry.addBreadcrumb("test") + + // when the clone is modified + val clonedScope = InternalSentrySdk.getCurrentScope()!! + clonedScope.clearBreadcrumbs() + + // then modifications should not be reflected + Sentry.configureScope { scope -> + assertEquals(1, scope.breadcrumbs.size) + } + } + @Test fun `serializeScope correctly creates top level map`() { val options = SentryAndroidOptions() diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index fd236052204..10c1b80af3a 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1205,6 +1205,7 @@ public final class io/sentry/SamplingContext { } public final class io/sentry/Scope { + public fun (Lio/sentry/Scope;)V public fun (Lio/sentry/SentryOptions;)V public fun addAttachment (Lio/sentry/Attachment;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V diff --git a/sentry/src/main/java/io/sentry/IHub.java b/sentry/src/main/java/io/sentry/IHub.java index 97583f66e8c..06f36f1a545 100644 --- a/sentry/src/main/java/io/sentry/IHub.java +++ b/sentry/src/main/java/io/sentry/IHub.java @@ -306,7 +306,11 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { void popScope(); /** - * Runs the callback with a new scope which gets dropped at the end + * Runs the callback with a new scope which gets dropped at the end. If you're using the Sentry + * SDK in globalHubMode (defaults to true on Android) {@link + * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope + * changes may be dropped when executed in parallel. Use {@link + * IHub#configureScope(ScopeCallback)} instead. * * @param callback the callback */ diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 01e57e6e9b2..bf4c92c1788 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -87,7 +87,8 @@ public Scope(final @NotNull SentryOptions options) { this.propagationContext = new PropagationContext(); } - Scope(final @NotNull Scope scope) { + @ApiStatus.Internal + public Scope(final @NotNull Scope scope) { this.transaction = scope.transaction; this.transactionName = scope.transactionName; this.session = scope.session;