diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a5869a88b1..e1a91cba211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Decouple Profiler from Transaction ([#3101](https://github.com/getsentry/sentry-java/pull/3101)) - Add options and sampling logic ([#3121](https://github.com/getsentry/sentry-java/pull/3121)) - Add ContentProvider and start profile ([#3128](https://github.com/getsentry/sentry-java/pull/3128)) +- Report process init time as a span for app start performance ([#3159](https://github.com/getsentry/sentry-java/pull/3159)) ### Fixes diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 7ebc195e81d..a586e3118df 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -424,6 +424,7 @@ public class io/sentry/android/core/performance/AppStartMetrics { public fun getAppStartTimeSpanWithFallback (Lio/sentry/android/core/SentryAndroidOptions;)Lio/sentry/android/core/performance/TimeSpan; public fun getAppStartType ()Lio/sentry/android/core/performance/AppStartMetrics$AppStartType; public fun getApplicationOnCreateTimeSpan ()Lio/sentry/android/core/performance/TimeSpan; + public fun getClassLoadedUptimeMs ()J public fun getContentProviderOnCreateTimeSpans ()Ljava/util/List; public static fun getInstance ()Lio/sentry/android/core/performance/AppStartMetrics; public fun getSdkInitTimeSpan ()Lio/sentry/android/core/performance/TimeSpan; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java index ec7e3b74d9c..20299ced08f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java @@ -33,6 +33,7 @@ final class PerformanceAndroidEventProcessor implements EventProcessor { private static final String APP_METRICS_CONTENT_PROVIDER_OP = "contentprovider.load"; private static final String APP_METRICS_ACTIVITIES_OP = "activity.load"; private static final String APP_METRICS_APPLICATION_OP = "application.load"; + private static final String APP_METRICS_PROCESS_INIT_OP = "process.load"; private boolean sentStartMeasurement = false; @@ -155,6 +156,23 @@ private void attachColdAppStartSpans( } } + // Process init + final long classInitUptimeMs = appStartMetrics.getClassLoadedUptimeMs(); + final @NotNull TimeSpan appStartTimeSpan = appStartMetrics.getAppStartTimeSpan(); + if (appStartTimeSpan.hasStarted()) { + final @NotNull TimeSpan processInitTimeSpan = new TimeSpan(); + processInitTimeSpan.setStartedAt(appStartTimeSpan.getStartUptimeMs()); + processInitTimeSpan.setStartUnixTimeMs(appStartTimeSpan.getStartTimestampMs()); + + processInitTimeSpan.setStoppedAt(classInitUptimeMs); + processInitTimeSpan.setDescription("Process Initialization"); + + txn.getSpans() + .add( + timeSpanToSentrySpan( + processInitTimeSpan, parentSpanId, traceId, APP_METRICS_PROCESS_INIT_OP)); + } + // Content Providers final @NotNull List contentProviderOnCreates = appStartMetrics.getContentProviderOnCreateTimeSpans(); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java b/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java index 06bd04b3dbd..6ddcbd16644 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java @@ -31,6 +31,8 @@ public enum AppStartType { WARM } + private static final long CLASS_LOADED_UPTIME_MS = SystemClock.uptimeMillis(); + private static volatile @Nullable AppStartMetrics instance; private @NotNull AppStartType appStartType = AppStartType.UNKNOWN; @@ -121,6 +123,10 @@ public void addActivityLifecycleTimeSpans(final @NotNull ActivityLifecycleTimeSp activityLifecycles.add(timeSpan); } + public long getClassLoadedUptimeMs() { + return CLASS_LOADED_UPTIME_MS; + } + /** * @return the app start time span if it was started and perf-2 is enabled, falls back to the sdk * init time span otherwise diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt index 1d98aae5390..934638ead31 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt @@ -246,6 +246,13 @@ class PerformanceAndroidEventProcessorTest { // then the app start metrics should be attached tr = sut.process(tr, Hint()) + assertTrue( + tr.spans.any { + "process.load" == it.op && + appStartSpan.spanId == it.parentSpanId + } + ) + assertTrue( tr.spans.any { "contentprovider.load" == it.op && diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt index 1f41f4cea81..0885024b003 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt @@ -12,6 +12,7 @@ import org.mockito.kotlin.mock import org.robolectric.annotation.Config import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotEquals import kotlin.test.assertNull import kotlin.test.assertSame import kotlin.test.assertTrue @@ -100,4 +101,9 @@ class AppStartMetricsTest { val sdkInitSpan = AppStartMetrics.getInstance().sdkInitTimeSpan assertSame(sdkInitSpan, timeSpan) } + + @Test + fun `class load time is set`() { + assertNotEquals(0, AppStartMetrics.getInstance().classLoadedUptimeMs) + } }