Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- The `ignoredErrors` option is now configurable via the manifest property `io.sentry.traces.ignored-errors` ([#4178](https://github.com/getsentry/sentry-java/pull/4178))
- A list of active Spring profiles is attached to payloads sent to Sentry (errors, traces, etc.) and displayed in the UI when using our Spring or Spring Boot integrations ([#4147](https://github.com/getsentry/sentry-java/pull/4147))
- This consists of an empty list when only the default profile is active
- Added `enableTraceIdGeneration` to the AndroidOptions. This allows Hybrid SDKs to "freeze" and control the trace and connect errors on different layers of the application ([4188](https://github.com/getsentry/sentry-java/pull/4188))
- Move to a single NetworkCallback listener to reduce number of IPC calls on Android ([#4164](https://github.com/getsentry/sentry-java/pull/4164))
- Add GraphQL Apollo Kotlin 4 integration ([#4166](https://github.com/getsentry/sentry-java/pull/4166))

Expand Down
2 changes: 2 additions & 0 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
public fun isEnableAppComponentBreadcrumbs ()Z
public fun isEnableAppLifecycleBreadcrumbs ()Z
public fun isEnableAutoActivityLifecycleTracing ()Z
public fun isEnableAutoTraceIdGeneration ()Z
public fun isEnableFramesTracking ()Z
public fun isEnableNdk ()Z
public fun isEnableNetworkEventBreadcrumbs ()Z
Expand All @@ -315,6 +316,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
public fun setEnableAppComponentBreadcrumbs (Z)V
public fun setEnableAppLifecycleBreadcrumbs (Z)V
public fun setEnableAutoActivityLifecycleTracing (Z)V
public fun setEnableAutoTraceIdGeneration (Z)V
public fun setEnableFramesTracking (Z)V
public fun setEnableNdk (Z)V
public fun setEnableNetworkEventBreadcrumbs (Z)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ private void startTracing(final @NotNull Activity activity) {
if (scopes != null && !isRunningTransactionOrTrace(activity)) {
if (!performanceEnabled) {
activitiesWithOngoingTransactions.put(activity, NoOpTransaction.getInstance());
TracingUtils.startNewTrace(scopes);
if (options.isEnableAutoTraceIdGeneration()) {
TracingUtils.startNewTrace(scopes);
}
} else {
// as we allow a single transaction running on the bound Scope, we finish the previous ones
stopPreviousTransactions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ final class ManifestMetadataReader {

static final String IGNORED_ERRORS = "io.sentry.ignored-errors";

static final String ENABLE_AUTO_TRACE_ID_GENERATION =
"io.sentry.traces.enable-auto-id-generation";

/** ManifestMetadataReader ctor */
private ManifestMetadataReader() {}

Expand Down Expand Up @@ -380,6 +383,13 @@ static void applyMetadata(
readBool(
metadata, logger, ENABLE_SCOPE_PERSISTENCE, options.isEnableScopePersistence()));

options.setEnableAutoTraceIdGeneration(
readBool(
metadata,
logger,
ENABLE_AUTO_TRACE_ID_GENERATION,
options.isEnableAutoTraceIdGeneration()));

if (options.getSessionReplay().getSessionSampleRate() == null) {
final Double sessionSampleRate =
readDouble(metadata, logger, REPLAYS_SESSION_SAMPLE_RATE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ public final class SentryAndroidOptions extends SentryOptions {
*/
private boolean enableScopeSync = true;

/**
* Whether to enable automatic trace ID generation. This is mainly used by the Hybrid SDKs to
* control the trace ID generation from the outside.
*/
private boolean enableAutoTraceIdGeneration = true;

public interface BeforeCaptureCallback {

/**
Expand Down Expand Up @@ -594,4 +600,12 @@ public void setFrameMetricsCollector(
final @Nullable SentryFrameMetricsCollector frameMetricsCollector) {
this.frameMetricsCollector = frameMetricsCollector;
}

public boolean isEnableAutoTraceIdGeneration() {
return enableAutoTraceIdGeneration;
}

public void setEnableAutoTraceIdGeneration(final boolean enableAutoTraceIdGeneration) {
this.enableAutoTraceIdGeneration = enableAutoTraceIdGeneration;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,9 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur

if (!(options.isTracingEnabled() && options.isEnableUserInteractionTracing())) {
if (isNewInteraction) {
TracingUtils.startNewTrace(scopes);
if (options.isEnableAutoTraceIdGeneration()) {
TracingUtils.startNewTrace(scopes);
}
activeUiElement = target;
activeEventType = eventType;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1385,10 +1385,11 @@ class ActivityLifecycleIntegrationTest {
}

@Test
fun `starts new trace if performance is disabled`() {
fun `starts new trace if performance is disabled and trace ID generation is enabled`() {
val sut = fixture.getSut()
val activity = mock<Activity>()
fixture.options.tracesSampleRate = null
fixture.options.isEnableAutoTraceIdGeneration = true

val argumentCaptor: ArgumentCaptor<ScopeCallback> = ArgumentCaptor.forClass(ScopeCallback::class.java)
val scope = Scope(fixture.options)
Expand All @@ -1405,6 +1406,28 @@ class ActivityLifecycleIntegrationTest {
assertNotSame(propagationContextAtStart, scope.propagationContext)
}

@Test
fun `does not start a new trace if performance is disabled and trace ID generation is disabled`() {
val sut = fixture.getSut()
val activity = mock<Activity>()
fixture.options.tracesSampleRate = null
fixture.options.isEnableAutoTraceIdGeneration = false

val argumentCaptor: ArgumentCaptor<ScopeCallback> = ArgumentCaptor.forClass(ScopeCallback::class.java)
val scope = Scope(fixture.options)
val propagationContextAtStart = scope.propagationContext
whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer {
argumentCaptor.value.run(scope)
}

sut.register(fixture.scopes, fixture.options)
sut.onActivityCreated(activity, fixture.bundle)

// once for the screen
verify(fixture.scopes).configureScope(any())
assertSame(propagationContextAtStart, scope.propagationContext)
}

@Test
fun `sets the activity as the current screen`() {
val sut = fixture.getSut()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

Expand All @@ -59,12 +60,14 @@ class SentryGestureListenerTracingTest {
hasViewIdInRes: Boolean = true,
tracesSampleRate: Double? = 1.0,
isEnableUserInteractionTracing: Boolean = true,
transaction: SentryTracer? = null
transaction: SentryTracer? = null,
isEnableAutoTraceIdGeneration: Boolean = true
): SentryGestureListener {
options.tracesSampleRate = tracesSampleRate
options.isEnableUserInteractionTracing = isEnableUserInteractionTracing
options.isEnableUserInteractionBreadcrumbs = true
options.gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true))
options.isEnableAutoTraceIdGeneration = isEnableAutoTraceIdGeneration

whenever(scopes.options).thenReturn(options)

Expand Down Expand Up @@ -370,6 +373,33 @@ class SentryGestureListenerTracingTest {
assertEquals(OUT_OF_RANGE, fixture.transaction.status)
}

@Test
fun `when tracing is disabled and auto trace id generation is disabled, does not start a new trace`() {
val sut = fixture.getSut<View>(tracesSampleRate = null, isEnableAutoTraceIdGeneration = false)

sut.onSingleTapUp(fixture.event)

verify(fixture.scopes, never()).configureScope(any())
}

@Test
fun `when tracing is disabled and auto trace id generation is enabled, starts a new trace`() {
val sut = fixture.getSut<View>(tracesSampleRate = null, isEnableAutoTraceIdGeneration = true)
val scope = Scope(fixture.options)
val initialPropagationContext = scope.propagationContext

sut.onSingleTapUp(fixture.event)

verify(fixture.scopes).configureScope(
check { callback ->
callback.run(scope)
// Verify that a new propagation context was set and it's different from the initial one
assertNotNull(scope.propagationContext)
assertNotEquals(initialPropagationContext, scope.propagationContext)
}
)
}

internal open class ScrollableListView : AbsListView(mock()) {
override fun getAdapter(): ListAdapter = mock()
override fun setSelection(position: Int) = Unit
Expand Down
Loading