diff --git a/CHANGELOG.md b/CHANGELOG.md index ce9260acd7b..cfd31db09c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - The Kotlin Language version is now set to 1.6 ([#3936](https://github.com/getsentry/sentry-java/pull/3936)) +### Features + +- Create onCreate and onStart spans for all Activities ([#4025](https://github.com/getsentry/sentry-java/pull/4025)) + ### Fixes - Do not log if `OtelContextScopesStorage` cannot be found ([#4127](https://github.com/getsentry/sentry-java/pull/4127)) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java index eaf05e619dc..3c0d8b3a5c7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java @@ -430,7 +430,8 @@ public void onActivityPostCreated( final @NotNull Activity activity, final @Nullable Bundle savedInstanceState) { final ActivityLifecycleSpanHelper helper = activitySpanHelpers.get(activity); if (helper != null) { - helper.createAndStopOnCreateSpan(appStartSpan); + helper.createAndStopOnCreateSpan( + appStartSpan != null ? appStartSpan : activitiesWithOngoingTransactions.get(activity)); } } @@ -468,7 +469,8 @@ public void onActivityStarted(final @NotNull Activity activity) { public void onActivityPostStarted(final @NotNull Activity activity) { final ActivityLifecycleSpanHelper helper = activitySpanHelpers.get(activity); if (helper != null) { - helper.createAndStopOnStartSpan(appStartSpan); + helper.createAndStopOnStartSpan( + appStartSpan != null ? appStartSpan : activitiesWithOngoingTransactions.get(activity)); // Needed to handle hybrid SDKs helper.saveSpanToAppStartMetrics(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java b/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java index 7fed5e0fdbe..fead459ba88 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java @@ -36,18 +36,18 @@ public void setOnStartStartTimestamp(final @NotNull SentryDate onStartStartTimes this.onStartStartTimestamp = onStartStartTimestamp; } - public void createAndStopOnCreateSpan(final @Nullable ISpan appStartSpan) { - if (onCreateStartTimestamp != null && appStartSpan != null) { + public void createAndStopOnCreateSpan(final @Nullable ISpan parentSpan) { + if (onCreateStartTimestamp != null && parentSpan != null) { onCreateSpan = - createLifecycleSpan(appStartSpan, activityName + ".onCreate", onCreateStartTimestamp); + createLifecycleSpan(parentSpan, activityName + ".onCreate", onCreateStartTimestamp); onCreateSpan.finish(); } } - public void createAndStopOnStartSpan(final @Nullable ISpan appStartSpan) { - if (onStartStartTimestamp != null && appStartSpan != null) { + public void createAndStopOnStartSpan(final @Nullable ISpan parentSpan) { + if (onStartStartTimestamp != null && parentSpan != null) { onStartSpan = - createLifecycleSpan(appStartSpan, activityName + ".onStart", onStartStartTimestamp); + createLifecycleSpan(parentSpan, activityName + ".onStart", onStartStartTimestamp); onStartSpan.finish(); } } @@ -106,19 +106,18 @@ public void saveSpanToAppStartMetrics() { } private @NotNull ISpan createLifecycleSpan( - final @NotNull ISpan appStartSpan, + final @NotNull ISpan parentSpan, final @NotNull String description, final @NotNull SentryDate startTimestamp) { final @NotNull ISpan span = - appStartSpan.startChild( + parentSpan.startChild( APP_METRICS_ACTIVITIES_OP, description, startTimestamp, Instrumenter.SENTRY); setDefaultStartSpanData(span); return span; } public void clear() { - // in case the appStartSpan isn't completed yet, we finish it as cancelled to avoid - // memory leak + // in case the parentSpan isn't completed yet, we finish it as cancelled to avoid memory leak if (onCreateSpan != null && !onCreateSpan.isFinished()) { onCreateSpan.finish(SpanStatus.CANCELLED); } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt index eae6e9cc1ce..a14f62c3f03 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt @@ -1561,6 +1561,43 @@ class ActivityLifecycleIntegrationTest { assertFalse(appStartMetrics.activityLifecycleTimeSpans.isEmpty()) } + @Test + fun `Creates activity lifecycle spans even when no app start span is available`() { + val sut = fixture.getSut() + fixture.options.tracesSampleRate = 1.0 + val startDate = SentryNanotimeDate(Date(2), 0) + val appStartMetrics = AppStartMetrics.getInstance() + val activity = mock() + fixture.options.dateProvider = SentryDateProvider { startDate } + // Don't set app start time, so there's no app start span + // setAppStartTime(appStartDate) + + sut.register(fixture.scopes, fixture.options) + assertTrue(sut.activitySpanHelpers.isEmpty()) + + sut.onActivityPreCreated(activity, null) + + assertFalse(sut.activitySpanHelpers.isEmpty()) + val helper = sut.activitySpanHelpers.values.first() + assertNotNull(helper.onCreateStartTimestamp) + + sut.onActivityCreated(activity, null) + assertNull(sut.appStartSpan) + + sut.onActivityPostCreated(activity, null) + assertTrue(helper.onCreateSpan!!.isFinished) + + sut.onActivityPreStarted(activity) + assertNotNull(helper.onStartStartTimestamp) + + sut.onActivityStarted(activity) + assertTrue(appStartMetrics.activityLifecycleTimeSpans.isEmpty()) + + sut.onActivityPostStarted(activity) + assertTrue(helper.onStartSpan!!.isFinished) + assertFalse(appStartMetrics.activityLifecycleTimeSpans.isEmpty()) + } + @Test fun `Save activity lifecycle spans in AppStartMetrics onPostSarted`() { val sut = fixture.getSut()