Skip to content

Commit 0d667ec

Browse files
authored
Add autoTransactionDeadlineTimeoutMillis option (#4555)
* Added deadlineTimeout option to configure auto transaction deadline timeout
1 parent 88f8434 commit 0d667ec

File tree

11 files changed

+229
-3
lines changed

11 files changed

+229
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Add deadlineTimeout option ([#4555](https://github.com/getsentry/sentry-java/pull/4555))
8+
59
### Fixes
610

711
- Allow multiple UncaughtExceptionHandlerIntegrations to be active at the same time ([#4462](https://github.com/getsentry/sentry-java/pull/4462))

sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,12 @@ private void startTracing(final @NotNull Activity activity) {
189189
}
190190

191191
final TransactionOptions transactionOptions = new TransactionOptions();
192+
193+
// Set deadline timeout based on configured option
194+
final long deadlineTimeoutMillis = options.getDeadlineTimeout();
195+
// No deadline when zero or negative value is set
192196
transactionOptions.setDeadlineTimeout(
193-
TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION);
197+
deadlineTimeoutMillis <= 0 ? null : deadlineTimeoutMillis);
194198

195199
if (options.isEnableActivityLifecycleTracingAutoFinish()) {
196200
transactionOptions.setIdleTimeout(options.getIdleTimeout());

sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ final class ManifestMetadataReader {
127127
static final String ENABLE_AUTO_TRACE_ID_GENERATION =
128128
"io.sentry.traces.enable-auto-id-generation";
129129

130+
static final String DEADLINE_TIMEOUT = "io.sentry.traces.deadline-timeout";
131+
130132
static final String FEEDBACK_NAME_REQUIRED = "io.sentry.feedback.is-name-required";
131133

132134
static final String FEEDBACK_SHOW_NAME = "io.sentry.feedback.show-name";
@@ -446,6 +448,9 @@ static void applyMetadata(
446448
ENABLE_AUTO_TRACE_ID_GENERATION,
447449
options.isEnableAutoTraceIdGeneration()));
448450

451+
options.setDeadlineTimeout(
452+
readLong(metadata, logger, DEADLINE_TIMEOUT, options.getDeadlineTimeout()));
453+
449454
if (options.getSessionReplay().getSessionSampleRate() == null) {
450455
final double sessionSampleRate =
451456
readDouble(metadata, logger, REPLAYS_SESSION_SAMPLE_RATE);

sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,13 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur
250250

251251
final TransactionOptions transactionOptions = new TransactionOptions();
252252
transactionOptions.setWaitForChildren(true);
253+
254+
// Set deadline timeout based on configured option
255+
final long deadlineTimeoutMillis = options.getDeadlineTimeout();
256+
// No deadline when zero or negative value is set
253257
transactionOptions.setDeadlineTimeout(
254-
TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION);
258+
deadlineTimeoutMillis <= 0 ? null : deadlineTimeoutMillis);
259+
255260
transactionOptions.setIdleTimeout(options.getIdleTimeout());
256261
transactionOptions.setTrimEnd(true);
257262
transactionOptions.setOrigin(TRACE_ORIGIN + "." + target.getOrigin());

sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,80 @@ class ActivityLifecycleIntegrationTest {
225225
)
226226
}
227227

228+
@Test
229+
fun `Activity transaction uses custom deadline timeout when autoTransactionDeadlineTimeoutMillis is set to positive value`() {
230+
val sut = fixture.getSut()
231+
fixture.options.tracesSampleRate = 1.0
232+
fixture.options.deadlineTimeout = 60000L // 60 seconds
233+
234+
sut.register(fixture.scopes, fixture.options)
235+
sut.onActivityCreated(mock(), fixture.bundle)
236+
237+
verify(fixture.scopes)
238+
.startTransaction(
239+
any<TransactionContext>(),
240+
check<TransactionOptions> { transactionOptions ->
241+
assertEquals(60000L, transactionOptions.deadlineTimeout)
242+
},
243+
)
244+
}
245+
246+
@Test
247+
fun `Activity transaction uses no deadline timeout when autoTransactionDeadlineTimeoutMillis is set to zero`() {
248+
val sut = fixture.getSut()
249+
fixture.options.tracesSampleRate = 1.0
250+
fixture.options.deadlineTimeout = 0L // No deadline
251+
252+
sut.register(fixture.scopes, fixture.options)
253+
sut.onActivityCreated(mock(), fixture.bundle)
254+
255+
verify(fixture.scopes)
256+
.startTransaction(
257+
any<TransactionContext>(),
258+
check<TransactionOptions> { transactionOptions ->
259+
assertNull(transactionOptions.deadlineTimeout)
260+
},
261+
)
262+
}
263+
264+
@Test
265+
fun `Activity transaction uses no deadline timeout when autoTransactionDeadlineTimeoutMillis is set to negative value`() {
266+
val sut = fixture.getSut()
267+
fixture.options.tracesSampleRate = 1.0
268+
fixture.options.deadlineTimeout = -1L // No deadline
269+
270+
sut.register(fixture.scopes, fixture.options)
271+
sut.onActivityCreated(mock(), fixture.bundle)
272+
273+
verify(fixture.scopes)
274+
.startTransaction(
275+
any<TransactionContext>(),
276+
check<TransactionOptions> { transactionOptions ->
277+
assertNull(transactionOptions.deadlineTimeout)
278+
},
279+
)
280+
}
281+
282+
@Test
283+
fun `Activity transaction uses default deadline timeout when autoTransactionDeadlineTimeoutMillis is default`() {
284+
val sut = fixture.getSut()
285+
fixture.options.tracesSampleRate = 1.0
286+
287+
sut.register(fixture.scopes, fixture.options)
288+
sut.onActivityCreated(mock(), fixture.bundle)
289+
290+
verify(fixture.scopes)
291+
.startTransaction(
292+
any<TransactionContext>(),
293+
check<TransactionOptions> { transactionOptions ->
294+
assertEquals(
295+
TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION,
296+
transactionOptions.deadlineTimeout,
297+
)
298+
},
299+
)
300+
}
301+
228302
@Test
229303
fun `Activity gets added to ActivityFramesTracker during transaction creation`() {
230304
val sut = fixture.getSut()

sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.sentry.ILogger
99
import io.sentry.ProfileLifecycle
1010
import io.sentry.SentryLevel
1111
import io.sentry.SentryReplayOptions
12+
import io.sentry.TransactionOptions
1213
import kotlin.test.BeforeTest
1314
import kotlin.test.Test
1415
import kotlin.test.assertEquals
@@ -1091,6 +1092,35 @@ class ManifestMetadataReaderTest {
10911092
assertEquals(expectedIdleTimeout.toLong(), fixture.options.idleTimeout)
10921093
}
10931094

1095+
@Test
1096+
fun `applyMetadata reads autoTransactionDeadlineTimeoutMillis from metadata`() {
1097+
// Arrange
1098+
val expectedTimeout = 60000
1099+
val bundle = bundleOf(ManifestMetadataReader.DEADLINE_TIMEOUT to expectedTimeout)
1100+
val context = fixture.getContext(metaData = bundle)
1101+
1102+
// Act
1103+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1104+
1105+
// Assert
1106+
assertEquals(expectedTimeout.toLong(), fixture.options.deadlineTimeout)
1107+
}
1108+
1109+
@Test
1110+
fun `applyMetadata reads autoTransactionDeadlineTimeoutMillis from metadata and keep default value if not found`() {
1111+
// Arrange
1112+
val context = fixture.getContext()
1113+
1114+
// Act
1115+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
1116+
1117+
// Assert
1118+
assertEquals(
1119+
TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION,
1120+
fixture.options.deadlineTimeout,
1121+
)
1122+
}
1123+
10941124
@Test
10951125
fun `applyMetadata without specifying idleTimeout, stays default`() {
10961126
// Arrange

sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,12 @@ constructor(
131131
TransactionOptions().also {
132132
it.isWaitForChildren = true
133133
it.idleTimeout = scopes.options.idleTimeout
134-
it.deadlineTimeout = TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION
134+
135+
// Set deadline timeout based on configured option
136+
val deadlineTimeoutMillis = scopes.options.deadlineTimeout
137+
// No deadline when zero or negative value is set
138+
it.deadlineTimeout = if (deadlineTimeoutMillis <= 0) null else deadlineTimeoutMillis
139+
135140
it.isTrimEnd = true
136141
}
137142

sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,48 @@ class SentryNavigationListenerTest {
399399
)
400400
}
401401

402+
@Test
403+
fun `Navigation listener uses custom deadline timeout when set to positive value`() {
404+
val sut = fixture.getSut()
405+
fixture.options.deadlineTimeout = 60000L
406+
407+
sut.onDestinationChanged(fixture.navController, fixture.destination, null)
408+
409+
verify(fixture.scopes)
410+
.startTransaction(
411+
any<TransactionContext>(),
412+
check<TransactionOptions> { options -> assertEquals(60000L, options.deadlineTimeout) },
413+
)
414+
}
415+
416+
@Test
417+
fun `Navigation listener uses no deadline timeout when set to zero`() {
418+
val sut = fixture.getSut()
419+
fixture.options.deadlineTimeout = 0L
420+
421+
sut.onDestinationChanged(fixture.navController, fixture.destination, null)
422+
423+
verify(fixture.scopes)
424+
.startTransaction(
425+
any<TransactionContext>(),
426+
check<TransactionOptions> { options -> assertNull(options.deadlineTimeout) },
427+
)
428+
}
429+
430+
@Test
431+
fun `Navigation listener uses no deadline timeout when set to negative value`() {
432+
val sut = fixture.getSut()
433+
fixture.options.deadlineTimeout = -1L
434+
435+
sut.onDestinationChanged(fixture.navController, fixture.destination, null)
436+
437+
verify(fixture.scopes)
438+
.startTransaction(
439+
any<TransactionContext>(),
440+
check<TransactionOptions> { options -> assertNull(options.deadlineTimeout) },
441+
)
442+
}
443+
402444
@Test
403445
fun `onDestinationChanged sets scope screen`() {
404446
val sut = fixture.getSut()

sentry/api/sentry.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3282,6 +3282,7 @@ public class io/sentry/SentryOptions {
32823282
public fun getContinuousProfiler ()Lio/sentry/IContinuousProfiler;
32833283
public fun getCron ()Lio/sentry/SentryOptions$Cron;
32843284
public fun getDateProvider ()Lio/sentry/SentryDateProvider;
3285+
public fun getDeadlineTimeout ()J
32853286
public fun getDebugMetaLoader ()Lio/sentry/internal/debugmeta/IDebugMetaLoader;
32863287
public fun getDefaultScopeType ()Lio/sentry/ScopeType;
32873288
public fun getDiagnosticLevel ()Lio/sentry/SentryLevel;
@@ -3412,6 +3413,7 @@ public class io/sentry/SentryOptions {
34123413
public fun setContinuousProfiler (Lio/sentry/IContinuousProfiler;)V
34133414
public fun setCron (Lio/sentry/SentryOptions$Cron;)V
34143415
public fun setDateProvider (Lio/sentry/SentryDateProvider;)V
3416+
public fun setDeadlineTimeout (J)V
34153417
public fun setDebug (Z)V
34163418
public fun setDebugMetaLoader (Lio/sentry/internal/debugmeta/IDebugMetaLoader;)V
34173419
public fun setDefaultScopeType (Lio/sentry/ScopeType;)V

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,16 @@ public class SentryOptions {
574574
*/
575575
private boolean startProfilerOnAppStart = false;
576576

577+
/**
578+
* Controls the deadline timeout in milliseconds for automatic transactions. When set to a
579+
* positive value, that value is used as the deadline timeout. When set to a value less than or
580+
* equal to 0, no deadline is applied and transactions will only finish when explicitly finished
581+
* or when the activity lifecycle ends.
582+
*
583+
* <p>Default is 30000 (30 seconds).
584+
*/
585+
private long deadlineTimeout = TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION;
586+
577587
private @NotNull SentryOptions.Logs logs = new SentryOptions.Logs();
578588

579589
private @NotNull ISocketTagger socketTagger = NoOpSocketTagger.getInstance();
@@ -2020,6 +2030,24 @@ public void setStartProfilerOnAppStart(final boolean startProfilerOnAppStart) {
20202030
this.startProfilerOnAppStart = startProfilerOnAppStart;
20212031
}
20222032

2033+
public long getDeadlineTimeout() {
2034+
return deadlineTimeout;
2035+
}
2036+
2037+
/**
2038+
* Controls the deadline timeout in milliseconds for automatic transactions. When set to a
2039+
* positive value, that value is used as the deadline timeout. When set to a value less than or
2040+
* equal to 0, no deadline is applied and transactions will only finish when explicitly finished
2041+
* or when the activity lifecycle ends.
2042+
*
2043+
* <p>Default is 30000 (30 seconds).
2044+
*
2045+
* @param deadlineTimeout the timeout in milliseconds
2046+
*/
2047+
public void setDeadlineTimeout(long deadlineTimeout) {
2048+
this.deadlineTimeout = deadlineTimeout;
2049+
}
2050+
20232051
/**
20242052
* Returns the profiling traces dir. path if set
20252053
*

0 commit comments

Comments
 (0)