diff --git a/CHANGELOG.md b/CHANGELOG.md index 09417cf844..a7020a7e3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Fix old profiles deletion on SDK init ([#3216](https://github.com/getsentry/sentry-java/pull/3216)) - Fix hub restore point in wrappers: SentryWrapper, SentryTaskDecorator and SentryScheduleHook ([#3225](https://github.com/getsentry/sentry-java/pull/3225)) - We now reset the hub to its previous value on the thread where the `Runnable`/`Callable`/`Supplier` is executed instead of setting it to the hub that was used on the thread where the `Runnable`/`Callable`/`Supplier` was created. +- Fix add missing thread name/id to app start spans ([#3226](https://github.com/getsentry/sentry-java/pull/3226)) ## 7.4.0 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 12419b4c90..00653b622e 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 @@ -4,11 +4,13 @@ import static io.sentry.android.core.ActivityLifecycleIntegration.APP_START_WARM; import static io.sentry.android.core.ActivityLifecycleIntegration.UI_LOAD_OP; +import android.os.Looper; import io.sentry.EventProcessor; import io.sentry.Hint; import io.sentry.MeasurementUnit; import io.sentry.SentryEvent; import io.sentry.SpanContext; +import io.sentry.SpanDataConvention; import io.sentry.SpanId; import io.sentry.SpanStatus; import io.sentry.android.core.performance.ActivityLifecycleTimeSpan; @@ -231,6 +233,11 @@ private static SentrySpan timeSpanToSentrySpan( final @Nullable SpanId parentSpanId, final @NotNull SentryId traceId, final @NotNull String operation) { + + final Map defaultSpanData = new HashMap<>(2); + defaultSpanData.put(SpanDataConvention.THREAD_ID, Looper.getMainLooper().getThread().getId()); + defaultSpanData.put(SpanDataConvention.THREAD_NAME, "main"); + return new SentrySpan( span.getStartTimestampSecs(), span.getProjectedStopTimestampSecs(), @@ -242,6 +249,6 @@ private static SentrySpan timeSpanToSentrySpan( SpanStatus.OK, APP_METRICS_ORIGIN, new HashMap<>(), - null); + defaultSpanData); } } 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 fac32d4e7d..9aaeb04770 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 @@ -1,6 +1,7 @@ package io.sentry.android.core import android.content.ContentProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Hint import io.sentry.IHub import io.sentry.MeasurementUnit @@ -18,6 +19,7 @@ import io.sentry.android.core.performance.AppStartMetrics.AppStartType import io.sentry.protocol.MeasurementValue import io.sentry.protocol.SentrySpan import io.sentry.protocol.SentryTransaction +import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @@ -27,6 +29,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +@RunWith(AndroidJUnit4::class) class PerformanceAndroidEventProcessorTest { private class Fixture { @@ -398,6 +401,61 @@ class PerformanceAndroidEventProcessorTest { ) } + @Test + fun `adds main thread name and id to app start spans`() { + // given some cold app start metrics + // where class loaded happened way before app start + val appStartMetrics = AppStartMetrics.getInstance() + appStartMetrics.appStartType = AppStartType.COLD + appStartMetrics.appStartTimeSpan.setStartedAt(1) + appStartMetrics.appStartTimeSpan.setStoppedAt(3000) + + AppStartMetrics.getInstance().applicationOnCreateTimeSpan.apply { + setStartedAt(1000) + description = "com.example.App.onCreate" + setStoppedAt(2000) + } + + val sut = fixture.getSut(enablePerformanceV2 = true) + val context = TransactionContext("Activity", UI_LOAD_OP) + val tracer = SentryTracer(context, fixture.hub) + var tr = SentryTransaction(tracer) + val appStartSpan = SentrySpan( + 0.0, + 1.0, + tr.contexts.trace!!.traceId, + SpanId(), + null, + APP_START_COLD, + "App Start", + SpanStatus.OK, + null, + emptyMap(), + null + ) + tr.spans.add(appStartSpan) + + // when the processor attaches the app start spans + tr = sut.process(tr, Hint()) + + // thread name and id should be set + assertTrue { + tr.spans.any { + it.op == "process.load" && + it.data!!["thread.name"] == "main" && + it.data!!.containsKey("thread.id") + } + } + + assertTrue { + tr.spans.any { + it.op == "application.load" && + it.data!!["thread.name"] == "main" && + it.data!!.containsKey("thread.id") + } + } + } + private fun setAppStart(options: SentryAndroidOptions, coldStart: Boolean = true) { AppStartMetrics.getInstance().apply { appStartType = when (coldStart) {