From 6d08e825851b2291dfd8f72e124aa0f22526d8ac Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 14 Nov 2022 09:02:10 +0100 Subject: [PATCH 01/13] Start a session after init if isEnableAutoSessionTracking is enabled Android-only, on top of that ensure that the LifecycleWatcher handles the existing session correctly --- .../sentry/android/core/LifecycleWatcher.java | 27 +++++-- .../io/sentry/android/core/SentryAndroid.java | 6 ++ .../android/core/LifecycleWatcherTest.kt | 79 ++++++++++++++++++- .../sentry/android/core/SentryAndroidTest.kt | 21 +++++ sentry/api/sentry.api | 1 + sentry/src/main/java/io/sentry/Scope.java | 5 ++ 6 files changed, 129 insertions(+), 10 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java index c2b100b7ff..d469abf2ff 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java @@ -5,6 +5,7 @@ import io.sentry.Breadcrumb; import io.sentry.IHub; import io.sentry.SentryLevel; +import io.sentry.Session; import io.sentry.transport.CurrentDateProvider; import io.sentry.transport.ICurrentDateProvider; import java.util.Timer; @@ -74,15 +75,25 @@ private void startSession() { cancelTask(); final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis(); - final long lastUpdatedSession = this.lastUpdatedSession.get(); - if (lastUpdatedSession == 0L - || (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) { - addSessionBreadcrumb("start"); - hub.startSession(); - runningSession.set(true); - } - this.lastUpdatedSession.set(currentTimeMillis); + hub.withScope( + scope -> { + long lastUpdatedSession = this.lastUpdatedSession.get(); + if (lastUpdatedSession == 0L) { + @Nullable Session currentSession = scope.getSession(); + if (currentSession != null && currentSession.getStarted() != null) { + lastUpdatedSession = currentSession.getStarted().getTime(); + } + } + + if (lastUpdatedSession == 0L + || (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) { + addSessionBreadcrumb("start"); + hub.startSession(); + runningSession.set(true); + } + this.lastUpdatedSession.set(currentTimeMillis); + }); } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 9bcc107d21..51458e2186 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -3,6 +3,7 @@ import android.content.Context; import android.os.SystemClock; import io.sentry.DateUtils; +import io.sentry.IHub; import io.sentry.ILogger; import io.sentry.Integration; import io.sentry.OptionsContainer; @@ -109,6 +110,11 @@ public static synchronized void init( resetEnvelopeCacheIfNeeded(options); }, true); + + final @NotNull IHub hub = Sentry.getCurrentHub(); + if (hub.getOptions().isEnableAutoSessionTracking()) { + hub.startSession(); + } } catch (IllegalAccessException e) { logger.log(SentryLevel.FATAL, "Fatal error during SentryAndroid.init(...)", e); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt index 4802b44da4..94e5ac0680 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt @@ -2,10 +2,16 @@ package io.sentry.android.core import androidx.lifecycle.LifecycleOwner import io.sentry.Breadcrumb +import io.sentry.DateUtils import io.sentry.IHub +import io.sentry.Scope +import io.sentry.ScopeCallback import io.sentry.SentryLevel +import io.sentry.Session +import io.sentry.Session.State import io.sentry.transport.ICurrentDateProvider import org.awaitility.kotlin.await +import org.mockito.ArgumentCaptor import org.mockito.kotlin.any import org.mockito.kotlin.check import org.mockito.kotlin.mock @@ -13,6 +19,7 @@ import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.util.* import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -25,8 +32,26 @@ class LifecycleWatcherTest { val hub = mock() val dateProvider = mock() - fun getSUT(sessionIntervalMillis: Long = 0L, enableAutoSessionTracking: Boolean = true, enableAppLifecycleBreadcrumbs: Boolean = true): LifecycleWatcher { - return LifecycleWatcher(hub, sessionIntervalMillis, enableAutoSessionTracking, enableAppLifecycleBreadcrumbs, dateProvider) + fun getSUT( + sessionIntervalMillis: Long = 0L, + enableAutoSessionTracking: Boolean = true, + enableAppLifecycleBreadcrumbs: Boolean = true, + session: Session? = null + ): LifecycleWatcher { + val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) + val scope = mock() + whenever(scope.session).thenReturn(session) + whenever(hub.withScope(argumentCaptor.capture())).thenAnswer { + argumentCaptor.value.run(scope) + } + + return LifecycleWatcher( + hub, + sessionIntervalMillis, + enableAutoSessionTracking, + enableAppLifecycleBreadcrumbs, + dateProvider + ) } } @@ -193,4 +218,54 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) assertNull(watcher.timer) } + + @Test + fun `if the hub has already a fresh session running, don't start new one`() { + val watcher = fixture.getSUT( + enableAppLifecycleBreadcrumbs = false, + session = Session( + State.Ok, + DateUtils.getCurrentDateTime(), + DateUtils.getCurrentDateTime(), + 0, + "abc", + UUID.fromString("3c1ffc32-f68f-4af2-a1ee-dd72f4d62d17"), + true, + 0, + 10.0, + null, + null, + null, + "release" + ) + ) + + watcher.onStart(fixture.ownerMock) + verify(fixture.hub, never()).startSession() + } + + @Test + fun `if the hub has a long running session, start new one`() { + val watcher = fixture.getSUT( + enableAppLifecycleBreadcrumbs = false, + session = Session( + State.Ok, + DateUtils.getDateTime(-1), + DateUtils.getDateTime(-1), + 0, + "abc", + UUID.fromString("3c1ffc32-f68f-4af2-a1ee-dd72f4d62d17"), + true, + 0, + 10.0, + null, + null, + null, + "release" + ) + ) + + watcher.onStart(fixture.ownerMock) + verify(fixture.hub).startSession() + } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt index a6d4f6cddf..68ade63e21 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt @@ -25,6 +25,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull +import kotlin.test.assertNull import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) @@ -163,6 +164,26 @@ class SentryAndroidTest { assertTrue { refOptions!!.envelopeDiskCache is CustomEnvelopCache } } + @Test + fun `init starts a session if auto session tracking is enabled`() { + fixture.initSut { options -> + options.isEnableAutoSessionTracking = true + } + Sentry.getCurrentHub().withScope { scope -> + assertNotNull(scope.session) + } + } + + @Test + fun `init does not start a session by if auto session tracking is disabled`() { + fixture.initSut { options -> + options.isEnableAutoSessionTracking = false + } + Sentry.getCurrentHub().withScope { scope -> + assertNull(scope.session) + } + } + private class CustomEnvelopCache : IEnvelopeCache { override fun iterator(): MutableIterator = TODO() override fun store(envelope: SentryEnvelope, hint: Hint) = Unit diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 4d5c9ee87f..dae3c11533 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -963,6 +963,7 @@ public final class io/sentry/Scope { public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getLevel ()Lio/sentry/SentryLevel; public fun getRequest ()Lio/sentry/protocol/Request; + public fun getSession ()Lio/sentry/Session; public fun getSpan ()Lio/sentry/ISpan; public fun getTags ()Ljava/util/Map; public fun getTransaction ()Lio/sentry/ITransaction; diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 0d5904fc6a..ac27668611 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -762,6 +762,11 @@ public void withTransaction(final @NotNull IWithTransaction callback) { } } + @ApiStatus.Internal + public @Nullable Session getSession() { + return session; + } + /** the IWithTransaction callback */ @ApiStatus.Internal public interface IWithTransaction { From 559cac2f153ebfb62a5f2b12ead3a81326be8f94 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 14 Nov 2022 09:20:51 +0100 Subject: [PATCH 02/13] Update Changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afc58b01c..310d1af621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +### Features +- Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356)) + ## 6.7.0 ### Fixes From b264169ed1b73b1151eea0c89fd914fd67a9eccc Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 14 Nov 2022 11:25:32 +0100 Subject: [PATCH 03/13] Update CHANGELOG.md Co-authored-by: Roman Zavarnitsyn --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbe5796141..58c94fdb94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ ### Features -- Update Spring Boot Jakarta to Spring Boot 3.0.0-RC2 ([#2347](https://github.com/getsentry/sentry-java/pull/2347)) +### Features + - Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356)) ## 6.7.0 From fba466a84750e41133bd2fdf121025f378b9bf29 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 14 Nov 2022 12:45:23 +0100 Subject: [PATCH 04/13] Fix Changelog --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eaca8775c..3c488fbebc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,7 @@ - Don't set device name on Android if `sendDefaultPii` is disabled ([#2354](https://github.com/getsentry/sentry-java/pull/2354)) ### Features - -### Features - +- Update Spring Boot Jakarta to Spring Boot 3.0.0-RC2 ([#2347](https://github.com/getsentry/sentry-java/pull/2347)) - Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356)) ## 6.7.0 From 7a7479b5b111f07e1084b251bbd1375e07ff78d2 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 14 Nov 2022 12:46:24 +0100 Subject: [PATCH 05/13] Fix Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c488fbebc..2df8284795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Don't set device name on Android if `sendDefaultPii` is disabled ([#2354](https://github.com/getsentry/sentry-java/pull/2354)) ### Features + - Update Spring Boot Jakarta to Spring Boot 3.0.0-RC2 ([#2347](https://github.com/getsentry/sentry-java/pull/2347)) - Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356)) From c7279287fa8b61fb0f808722dd4485d6ca436047 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 21 Nov 2022 21:49:25 +0100 Subject: [PATCH 06/13] Remove obsolete LifecycleWatcher.runningSession --- .../java/io/sentry/android/core/LifecycleWatcher.java | 10 ---------- .../io/sentry/android/core/LifecycleWatcherTest.kt | 8 +++----- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java index d469abf2ff..b2f815204e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java @@ -10,7 +10,6 @@ import io.sentry.transport.ICurrentDateProvider; import java.util.Timer; import java.util.TimerTask; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,7 +27,6 @@ final class LifecycleWatcher implements DefaultLifecycleObserver { private final @NotNull IHub hub; private final boolean enableSessionTracking; private final boolean enableAppLifecycleBreadcrumbs; - private final @NotNull AtomicBoolean runningSession = new AtomicBoolean(); private final @NotNull ICurrentDateProvider currentDateProvider; @@ -90,7 +88,6 @@ private void startSession() { || (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) { addSessionBreadcrumb("start"); hub.startSession(); - runningSession.set(true); } this.lastUpdatedSession.set(currentTimeMillis); }); @@ -121,7 +118,6 @@ private void scheduleEndSession() { public void run() { addSessionBreadcrumb("end"); hub.endSession(); - runningSession.set(false); } }; @@ -159,12 +155,6 @@ private void addSessionBreadcrumb(final @NotNull String state) { hub.addBreadcrumb(breadcrumb); } - @TestOnly - @NotNull - AtomicBoolean isRunningSession() { - return runningSession; - } - @TestOnly @Nullable TimerTask getTimerTask() { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt index 94e5ac0680..740d340a7e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt @@ -10,12 +10,12 @@ import io.sentry.SentryLevel import io.sentry.Session import io.sentry.Session.State import io.sentry.transport.ICurrentDateProvider -import org.awaitility.kotlin.await import org.mockito.ArgumentCaptor import org.mockito.kotlin.any import org.mockito.kotlin.check import org.mockito.kotlin.mock import org.mockito.kotlin.never +import org.mockito.kotlin.timeout import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -87,8 +87,7 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) watcher.onStop(fixture.ownerMock) - await.untilFalse(watcher.isRunningSession) - verify(fixture.hub).endSession() + verify(fixture.hub, timeout(10000)).endSession() } @Test @@ -137,9 +136,8 @@ class LifecycleWatcherTest { @Test fun `When session tracking is enabled, add breadcrumb on stop`() { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) - watcher.isRunningSession.set(true) watcher.onStop(fixture.ownerMock) - await.untilFalse(watcher.isRunningSession) + verify(fixture.hub, timeout(10000)).endSession() verify(fixture.hub).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) From 504068c51d4a755d57a2bc71c58a64eec985a7b5 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 22 Nov 2022 07:56:05 +0100 Subject: [PATCH 07/13] Emit session.start breadcrumb on initial Hub.startSession --- .../sentry/android/core/LifecycleWatcher.java | 7 ++----- .../io/sentry/android/core/SentryAndroid.java | 2 ++ .../core/internal/util/BreadcrumbFactory.java | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 sentry-android-core/src/main/java/io/sentry/android/core/internal/util/BreadcrumbFactory.java diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java index b2f815204e..995d7f9f19 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java @@ -6,6 +6,7 @@ import io.sentry.IHub; import io.sentry.SentryLevel; import io.sentry.Session; +import io.sentry.android.core.internal.util.BreadcrumbFactory; import io.sentry.transport.CurrentDateProvider; import io.sentry.transport.ICurrentDateProvider; import java.util.Timer; @@ -147,11 +148,7 @@ private void addAppBreadcrumb(final @NotNull String state) { } private void addSessionBreadcrumb(final @NotNull String state) { - final Breadcrumb breadcrumb = new Breadcrumb(); - breadcrumb.setType("session"); - breadcrumb.setData("state", state); - breadcrumb.setCategory("app.lifecycle"); - breadcrumb.setLevel(SentryLevel.INFO); + final Breadcrumb breadcrumb = BreadcrumbFactory.forSession(state); hub.addBreadcrumb(breadcrumb); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 87ba2d3f4e..9d046cbdb7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -10,6 +10,7 @@ import io.sentry.Sentry; import io.sentry.SentryLevel; import io.sentry.SentryOptions; +import io.sentry.android.core.internal.util.BreadcrumbFactory; import io.sentry.android.fragment.FragmentLifecycleIntegration; import io.sentry.android.timber.SentryTimberIntegration; import java.lang.reflect.InvocationTargetException; @@ -123,6 +124,7 @@ public static synchronized void init( final @NotNull IHub hub = Sentry.getCurrentHub(); if (hub.getOptions().isEnableAutoSessionTracking()) { + hub.addBreadcrumb(BreadcrumbFactory.forSession("session.start")); hub.startSession(); } } catch (IllegalAccessException e) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/BreadcrumbFactory.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/BreadcrumbFactory.java new file mode 100644 index 0000000000..04cabc9430 --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/BreadcrumbFactory.java @@ -0,0 +1,19 @@ +package io.sentry.android.core.internal.util; + +import io.sentry.Breadcrumb; +import io.sentry.SentryLevel; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +public class BreadcrumbFactory { + + public static @NotNull Breadcrumb forSession(@NotNull String state) { + final Breadcrumb breadcrumb = new Breadcrumb(); + breadcrumb.setType("session"); + breadcrumb.setData("state", state); + breadcrumb.setCategory("app.lifecycle"); + breadcrumb.setLevel(SentryLevel.INFO); + return breadcrumb; + } +} From be4fa27ee537dad66b4843f181faa9b32e4c9df2 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 23 Nov 2022 15:27:33 +0100 Subject: [PATCH 08/13] Add integration for Compose clickables --- .../android/core/SentryAndroidOptions.java | 23 +----- .../compose/SentryClickableIntegration.kt | 77 +++++++++++++++++++ .../main/java/io/sentry/SentryOptions.java | 22 ++++++ 3 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index d58ea76314..66916a7103 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -39,8 +39,6 @@ public final class SentryAndroidOptions extends SentryOptions { /** Enable or disable automatic breadcrumbs for App Components Using ComponentCallbacks */ private boolean enableAppComponentBreadcrumbs = true; - /** Enable or disable automatic breadcrumbs for User interactions Using Window.Callback */ - private boolean enableUserInteractionBreadcrumbs = true; /** * Enables the Auto instrumentation for Activity lifecycle tracing. @@ -93,9 +91,6 @@ public final class SentryAndroidOptions extends SentryOptions { */ private int profilingTracesHz = 101; - /** Enables the Auto instrumentation for user interaction tracing. */ - private boolean enableUserInteractionTracing = false; - /** Interface that loads the debug images list */ private @NotNull IDebugImagesLoader debugImagesLoader = NoOpDebugImagesLoader.getInstance(); @@ -241,14 +236,6 @@ public void setEnableAppComponentBreadcrumbs(boolean enableAppComponentBreadcrum this.enableAppComponentBreadcrumbs = enableAppComponentBreadcrumbs; } - public boolean isEnableUserInteractionBreadcrumbs() { - return enableUserInteractionBreadcrumbs; - } - - public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBreadcrumbs) { - this.enableUserInteractionBreadcrumbs = enableUserInteractionBreadcrumbs; - } - /** * Enable or disable all the automatic breadcrumbs * @@ -259,7 +246,7 @@ public void enableAllAutoBreadcrumbs(boolean enable) { enableAppComponentBreadcrumbs = enable; enableSystemEventBreadcrumbs = enable; enableAppLifecycleBreadcrumbs = enable; - enableUserInteractionBreadcrumbs = enable; + setEnableUserInteractionBreadcrumbs(enable); } /** @@ -343,14 +330,6 @@ public void setAttachScreenshot(boolean attachScreenshot) { this.attachScreenshot = attachScreenshot; } - public boolean isEnableUserInteractionTracing() { - return enableUserInteractionTracing; - } - - public void setEnableUserInteractionTracing(boolean enableUserInteractionTracing) { - this.enableUserInteractionTracing = enableUserInteractionTracing; - } - public boolean isCollectAdditionalContext() { return collectAdditionalContext; } diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt new file mode 100644 index 0000000000..97a6a6b6d1 --- /dev/null +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt @@ -0,0 +1,77 @@ +package io.sentry.compose + +import io.sentry.Breadcrumb +import io.sentry.IHub +import io.sentry.ITransaction +import io.sentry.Scope +import io.sentry.Sentry +import io.sentry.SentryLevel +import io.sentry.TransactionContext +import io.sentry.TransactionOptions +import io.sentry.protocol.TransactionNameSource + +public fun wrapClickable(clickable: () -> Unit, clickLabel: String?): () -> Unit { + return { + val hub = Sentry.getCurrentHub() + if (clickLabel.isNullOrEmpty()) { + hub.options + .logger + .log( + SentryLevel.DEBUG, + "Modifier.clickable clickLabel is null, skipping breadcrumb/transaction creation.", + ) + } else { + addBreadcrumb(hub, clickLabel) + startTransaction(hub, clickLabel) + } + + clickable() + } +} + +private fun addBreadcrumb(hub: IHub, label: String) { + if (!hub.options.isEnableUserInteractionBreadcrumbs) { + return + } + + val breadcrumb = Breadcrumb.userInteraction( + "action.click", + label, + null, + emptyMap() + ) + hub.addBreadcrumb(breadcrumb) +} + +private fun startTransaction(hub: IHub, label: String) { + if (!(hub.options.isTracingEnabled && hub.options.isEnableUserInteractionTracing)) { + return + } + + val transactionOptions = TransactionOptions().apply { + isWaitForChildren = true + idleTimeout = hub.options.idleTimeout + isTrimEnd = true + } + + val transaction: ITransaction = hub.startTransaction( + TransactionContext(label, TransactionNameSource.COMPONENT, "ui.action.click"), + transactionOptions + ) + + hub.configureScope { scope: Scope? -> + scope?.withTransaction { scopeTransaction: ITransaction? -> + if (scopeTransaction == null) { + scope.transaction = transaction + } else { + hub.options + .logger + .log( + SentryLevel.DEBUG, + "Transaction '%s' won't be bound to the Scope since there's one already in there.", + transaction.name + ) + } + } + } +} diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index e7c0980c2b..b5a3d1fe68 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -361,6 +361,12 @@ public class SentryOptions { /** Modules (dependencies, packages) that will be send along with each event. */ private @NotNull IModulesLoader modulesLoader = NoOpModulesLoader.getInstance(); + /** Enables the Auto instrumentation for user interaction tracing. */ + private boolean enableUserInteractionTracing = false; + + /** Enable or disable automatic breadcrumbs for User interactions Using Window.Callback */ + private boolean enableUserInteractionBreadcrumbs = true; + /** * Adds an event processor * @@ -1741,6 +1747,22 @@ public void setSendClientReports(boolean sendClientReports) { } } + public boolean isEnableUserInteractionTracing() { + return enableUserInteractionTracing; + } + + public void setEnableUserInteractionTracing(boolean enableUserInteractionTracing) { + this.enableUserInteractionTracing = enableUserInteractionTracing; + } + + public boolean isEnableUserInteractionBreadcrumbs() { + return enableUserInteractionBreadcrumbs; + } + + public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBreadcrumbs) { + this.enableUserInteractionBreadcrumbs = enableUserInteractionBreadcrumbs; + } + /** * Returns a ClientReportRecorder or a NoOp if sending of client reports has been disabled. * From 25a7da9ba634ee7f6d9ef8b5b1c353993334e95c Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 23 Nov 2022 15:28:23 +0100 Subject: [PATCH 09/13] Fix only create breadcrumbs when option is enabled --- .../core/internal/gestures/SentryGestureListener.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java index a076bd6bf4..40c8310643 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java @@ -181,6 +181,11 @@ private void addBreadcrumb( final @NotNull String eventType, final @NotNull Map additionalData, final @NotNull MotionEvent motionEvent) { + + if (hub.getOptions().isEnableUserInteractionBreadcrumbs()) { + return; + } + @NotNull String className; @Nullable String canonicalName = target.getClass().getCanonicalName(); if (canonicalName != null) { From 3143955002233f9bca183e572d66d1423f1fb47b Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 13 Dec 2022 10:30:36 +0100 Subject: [PATCH 10/13] Update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23f16a6649..a7a55c2ee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Features + +- Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356)) + ### Dependencies - Bump Native SDK from v0.5.2 to v0.5.3 ([#2423](https://github.com/getsentry/sentry-java/pull/2423)) @@ -76,7 +80,6 @@ ### Features - Update Spring Boot Jakarta to Spring Boot 3.0.0-RC2 ([#2347](https://github.com/getsentry/sentry-java/pull/2347)) -- Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356)) ## 6.7.0 From 095b44b089c51b1458150f500546f6346b550cac Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 13 Dec 2022 10:35:50 +0100 Subject: [PATCH 11/13] Remove unneeded SentryClickableIntegration --- sentry-compose/api/android/sentry-compose.api | 4 - .../compose/SentryClickableIntegration.kt | 77 ------------------- 2 files changed, 81 deletions(-) delete mode 100644 sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt diff --git a/sentry-compose/api/android/sentry-compose.api b/sentry-compose/api/android/sentry-compose.api index bb0f41fe73..e30b863f6b 100644 --- a/sentry-compose/api/android/sentry-compose.api +++ b/sentry-compose/api/android/sentry-compose.api @@ -6,10 +6,6 @@ public final class io/sentry/compose/BuildConfig { public fun ()V } -public final class io/sentry/compose/SentryClickableIntegrationKt { - public static final fun wrapClickable (Lkotlin/jvm/functions/Function0;Ljava/lang/String;)Lkotlin/jvm/functions/Function0; -} - public final class io/sentry/compose/SentryNavigationIntegrationKt { public static final fun withSentryObservableEffect (Landroidx/navigation/NavHostController;ZZLandroidx/compose/runtime/Composer;II)Landroidx/navigation/NavHostController; } diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt deleted file mode 100644 index 27658450be..0000000000 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryClickableIntegration.kt +++ /dev/null @@ -1,77 +0,0 @@ -package io.sentry.compose - -import io.sentry.Breadcrumb -import io.sentry.IHub -import io.sentry.ITransaction -import io.sentry.Scope -import io.sentry.Sentry -import io.sentry.SentryLevel -import io.sentry.TransactionContext -import io.sentry.TransactionOptions -import io.sentry.protocol.TransactionNameSource - -public fun wrapClickable(clickable: () -> Unit, clickLabel: String?): () -> Unit { - return { - val hub = Sentry.getCurrentHub() - if (clickLabel.isNullOrEmpty()) { - hub.options - .logger - .log( - SentryLevel.DEBUG, - "Modifier.clickable clickLabel is null, skipping breadcrumb/transaction creation." - ) - } else { - addBreadcrumb(hub, clickLabel) - startTransaction(hub, clickLabel) - } - - clickable() - } -} - -private fun addBreadcrumb(hub: IHub, label: String) { - if (!hub.options.isEnableUserInteractionBreadcrumbs) { - return - } - - val breadcrumb = Breadcrumb.userInteraction( - "action.click", - label, - null, - emptyMap() - ) - hub.addBreadcrumb(breadcrumb) -} - -private fun startTransaction(hub: IHub, label: String) { - if (!(hub.options.isTracingEnabled && hub.options.isEnableUserInteractionTracing)) { - return - } - - val transactionOptions = TransactionOptions().apply { - isWaitForChildren = true - idleTimeout = hub.options.idleTimeout - isTrimEnd = true - } - - val transaction: ITransaction = hub.startTransaction( - TransactionContext(label, TransactionNameSource.COMPONENT, "ui.action.click"), - transactionOptions - ) - - hub.configureScope { scope: Scope? -> - scope?.withTransaction { scopeTransaction: ITransaction? -> - if (scopeTransaction == null) { - scope.transaction = transaction - } else { - hub.options - .logger - .log( - SentryLevel.DEBUG, - "Transaction '%s' won't be bound to the Scope since there's one already in there.", - transaction.name - ) - } - } - } -} From ddb52c31dff1c20af12e257e725ad66a44f94030 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 13 Dec 2022 11:05:36 +0100 Subject: [PATCH 12/13] Fix missing negation for checking user interaction breadcrumbs --- .../android/core/internal/gestures/SentryGestureListener.java | 2 +- .../core/internal/gestures/SentryGestureListenerScrollTest.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java index 40c8310643..72a2d3983c 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java @@ -182,7 +182,7 @@ private void addBreadcrumb( final @NotNull Map additionalData, final @NotNull MotionEvent motionEvent) { - if (hub.getOptions().isEnableUserInteractionBreadcrumbs()) { + if ((!options.isEnableUserInteractionBreadcrumbs())) { return; } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt index 7e935893ae..414538c657 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt @@ -34,6 +34,7 @@ class SentryGestureListenerScrollTest { val resources = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" + isEnableUserInteractionBreadcrumbs = true } val hub = mock() From 90688a29973c9b6b600abdec1838e4cf13be495c Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 13 Dec 2022 13:09:23 +0100 Subject: [PATCH 13/13] Revert non-required options move --- .../api/sentry-android-core.api | 4 ++++ .../android/core/SentryAndroidOptions.java | 24 ++++++++++++++++++- sentry/api/sentry.api | 4 ---- .../main/java/io/sentry/SentryOptions.java | 22 ----------------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 278581e1f2..17dca925d3 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -159,6 +159,8 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun isEnableAutoActivityLifecycleTracing ()Z public fun isEnableFramesTracking ()Z public fun isEnableSystemEventBreadcrumbs ()Z + public fun isEnableUserInteractionBreadcrumbs ()Z + public fun isEnableUserInteractionTracing ()Z public fun setAnrEnabled (Z)V public fun setAnrReportInDebug (Z)V public fun setAnrTimeoutIntervalMillis (J)V @@ -172,6 +174,8 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun setEnableAutoActivityLifecycleTracing (Z)V public fun setEnableFramesTracking (Z)V public fun setEnableSystemEventBreadcrumbs (Z)V + public fun setEnableUserInteractionBreadcrumbs (Z)V + public fun setEnableUserInteractionTracing (Z)V public fun setProfilingTracesHz (I)V public fun setProfilingTracesIntervalMillis (I)V } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index ca1f9a03c1..d58ea76314 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -39,6 +39,9 @@ public final class SentryAndroidOptions extends SentryOptions { /** Enable or disable automatic breadcrumbs for App Components Using ComponentCallbacks */ private boolean enableAppComponentBreadcrumbs = true; + /** Enable or disable automatic breadcrumbs for User interactions Using Window.Callback */ + private boolean enableUserInteractionBreadcrumbs = true; + /** * Enables the Auto instrumentation for Activity lifecycle tracing. * @@ -90,6 +93,9 @@ public final class SentryAndroidOptions extends SentryOptions { */ private int profilingTracesHz = 101; + /** Enables the Auto instrumentation for user interaction tracing. */ + private boolean enableUserInteractionTracing = false; + /** Interface that loads the debug images list */ private @NotNull IDebugImagesLoader debugImagesLoader = NoOpDebugImagesLoader.getInstance(); @@ -235,6 +241,14 @@ public void setEnableAppComponentBreadcrumbs(boolean enableAppComponentBreadcrum this.enableAppComponentBreadcrumbs = enableAppComponentBreadcrumbs; } + public boolean isEnableUserInteractionBreadcrumbs() { + return enableUserInteractionBreadcrumbs; + } + + public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBreadcrumbs) { + this.enableUserInteractionBreadcrumbs = enableUserInteractionBreadcrumbs; + } + /** * Enable or disable all the automatic breadcrumbs * @@ -245,7 +259,7 @@ public void enableAllAutoBreadcrumbs(boolean enable) { enableAppComponentBreadcrumbs = enable; enableSystemEventBreadcrumbs = enable; enableAppLifecycleBreadcrumbs = enable; - setEnableUserInteractionBreadcrumbs(enable); + enableUserInteractionBreadcrumbs = enable; } /** @@ -329,6 +343,14 @@ public void setAttachScreenshot(boolean attachScreenshot) { this.attachScreenshot = attachScreenshot; } + public boolean isEnableUserInteractionTracing() { + return enableUserInteractionTracing; + } + + public void setEnableUserInteractionTracing(boolean enableUserInteractionTracing) { + this.enableUserInteractionTracing = enableUserInteractionTracing; + } + public boolean isCollectAdditionalContext() { return collectAdditionalContext; } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index c4bd7b894d..be126c57cb 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1445,8 +1445,6 @@ public class io/sentry/SentryOptions { public fun isEnableScopeSync ()Z public fun isEnableShutdownHook ()Z public fun isEnableUncaughtExceptionHandler ()Z - public fun isEnableUserInteractionBreadcrumbs ()Z - public fun isEnableUserInteractionTracing ()Z public fun isPrintUncaughtStackTrace ()Z public fun isProfilingEnabled ()Z public fun isSendClientReports ()Z @@ -1474,8 +1472,6 @@ public class io/sentry/SentryOptions { public fun setEnableScopeSync (Z)V public fun setEnableShutdownHook (Z)V public fun setEnableUncaughtExceptionHandler (Z)V - public fun setEnableUserInteractionBreadcrumbs (Z)V - public fun setEnableUserInteractionTracing (Z)V public fun setEnvelopeDiskCache (Lio/sentry/cache/IEnvelopeCache;)V public fun setEnvelopeReader (Lio/sentry/IEnvelopeReader;)V public fun setEnvironment (Ljava/lang/String;)V diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 02ffb1b46d..4b5543d601 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -368,12 +368,6 @@ public class SentryOptions { /** Modules (dependencies, packages) that will be send along with each event. */ private @NotNull IModulesLoader modulesLoader = NoOpModulesLoader.getInstance(); - /** Enables the Auto instrumentation for user interaction tracing. */ - private boolean enableUserInteractionTracing = false; - - /** Enable or disable automatic breadcrumbs for User interactions Using Window.Callback */ - private boolean enableUserInteractionBreadcrumbs = true; - /** Which framework is responsible for instrumenting. */ private @NotNull Instrumenter instrumenter = Instrumenter.SENTRY; @@ -1776,22 +1770,6 @@ public void setSendClientReports(boolean sendClientReports) { } } - public boolean isEnableUserInteractionTracing() { - return enableUserInteractionTracing; - } - - public void setEnableUserInteractionTracing(boolean enableUserInteractionTracing) { - this.enableUserInteractionTracing = enableUserInteractionTracing; - } - - public boolean isEnableUserInteractionBreadcrumbs() { - return enableUserInteractionBreadcrumbs; - } - - public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBreadcrumbs) { - this.enableUserInteractionBreadcrumbs = enableUserInteractionBreadcrumbs; - } - /** * Sets the instrumenter used for performance instrumentation. *