Skip to content

Commit 2fd32ed

Browse files
authored
Factor out and make session timeouts more explicit. (#887)
* factor out and make session timeouts more explicit. * fix build after rebase
1 parent b8bfa1f commit 2fd32ed

File tree

10 files changed

+80
-38
lines changed

10 files changed

+80
-38
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### ⚠️⚠️ Breaking changes
6+
7+
- Remove `setSessionTimeout()` on `OtelRumConfig` in favor of new `setSessionConfig()`.([#xxx](https://github.com/open-telemetry/opentelemetry-android/pull/xxx))
8+
59
## Version 0.10.0 (2025-03-06)
610

711
- This version builds on opentelemetry-java-instrumentation

core/src/main/java/io/opentelemetry/android/OpenTelemetryRum.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import android.app.Application;
99
import io.opentelemetry.android.config.OtelRumConfig;
1010
import io.opentelemetry.android.internal.services.Services;
11+
import io.opentelemetry.android.internal.session.SessionIdTimeoutHandler;
12+
import io.opentelemetry.android.internal.session.SessionManagerImpl;
13+
import io.opentelemetry.android.session.SessionManager;
1114
import io.opentelemetry.api.OpenTelemetry;
1215
import io.opentelemetry.sdk.OpenTelemetrySdk;
1316
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
@@ -64,8 +67,17 @@ static OpenTelemetryRumBuilder builder(Application application, OtelRumConfig co
6467
static SdkPreconfiguredRumBuilder builder(
6568
Application application, OpenTelemetrySdk openTelemetrySdk, OtelRumConfig config) {
6669

70+
SessionIdTimeoutHandler timeoutHandler =
71+
new SessionIdTimeoutHandler(config.getSessionConfig());
72+
SessionManager sessionManager =
73+
SessionManagerImpl.create(timeoutHandler, config.getSessionConfig());
6774
return new SdkPreconfiguredRumBuilder(
68-
application, openTelemetrySdk, config, Services.get(application));
75+
application,
76+
openTelemetrySdk,
77+
timeoutHandler,
78+
sessionManager,
79+
config,
80+
Services.get(application));
6981
}
7082

7183
/** Returns a no-op implementation of {@link OpenTelemetryRum}. */

core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import io.opentelemetry.android.internal.services.periodicwork.PeriodicWork;
3838
import io.opentelemetry.android.internal.session.SessionIdTimeoutHandler;
3939
import io.opentelemetry.android.internal.session.SessionManagerImpl;
40+
import io.opentelemetry.android.session.SessionConfig;
4041
import io.opentelemetry.android.session.SessionManager;
4142
import io.opentelemetry.android.session.SessionProvider;
4243
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
@@ -117,8 +118,9 @@ private static TextMapPropagator buildDefaultPropagator() {
117118
}
118119

119120
public static OpenTelemetryRumBuilder create(Application application, OtelRumConfig config) {
120-
return new OpenTelemetryRumBuilder(
121-
application, config, new SessionIdTimeoutHandler(config.getSessionTimeout()));
121+
SessionConfig sessionConfig = config.getSessionConfig();
122+
SessionIdTimeoutHandler timeoutHandler = new SessionIdTimeoutHandler(sessionConfig);
123+
return new OpenTelemetryRumBuilder(application, config, timeoutHandler);
122124
}
123125

124126
OpenTelemetryRumBuilder(
@@ -320,8 +322,7 @@ public OpenTelemetryRum build() {
320322
new BufferDelegatingMetricExporter();
321323

322324
if (sessionManager == null) {
323-
sessionManager =
324-
SessionManagerImpl.create(timeoutHandler, config.getSessionTimeout().toNanos());
325+
sessionManager = SessionManagerImpl.create(timeoutHandler, config.getSessionConfig());
325326
}
326327

327328
OpenTelemetrySdk sdk =

core/src/main/java/io/opentelemetry/android/SdkPreconfiguredRumBuilder.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import io.opentelemetry.android.instrumentation.AndroidInstrumentationLoader
1212
import io.opentelemetry.android.instrumentation.InstallationContext
1313
import io.opentelemetry.android.internal.services.Services
1414
import io.opentelemetry.android.internal.session.SessionIdTimeoutHandler
15-
import io.opentelemetry.android.internal.session.SessionManagerImpl
1615
import io.opentelemetry.android.session.SessionManager
1716
import io.opentelemetry.sdk.OpenTelemetrySdk
1817

@@ -21,8 +20,8 @@ class SdkPreconfiguredRumBuilder
2120
internal constructor(
2221
private val application: Application,
2322
private val sdk: OpenTelemetrySdk,
24-
private val timeoutHandler: SessionIdTimeoutHandler = SessionIdTimeoutHandler(),
25-
private val sessionManager: SessionManager = SessionManagerImpl(timeoutHandler = timeoutHandler),
23+
private val timeoutHandler: SessionIdTimeoutHandler,
24+
private val sessionManager: SessionManager,
2625
private val config: OtelRumConfig,
2726
private val services: Services,
2827
) {

core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java

+13-8
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import io.opentelemetry.android.ScreenAttributesSpanProcessor;
99
import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfig;
1010
import io.opentelemetry.android.internal.services.network.CurrentNetworkProvider;
11+
import io.opentelemetry.android.session.SessionConfig;
1112
import io.opentelemetry.api.common.Attributes;
12-
import java.time.Duration;
1313
import java.util.ArrayList;
1414
import java.util.List;
1515
import java.util.function.Supplier;
@@ -27,7 +27,7 @@ public class OtelRumConfig {
2727
private boolean includeScreenAttributes = true;
2828
private boolean discoverInstrumentations = true;
2929
private DiskBufferingConfig diskBufferingConfig = DiskBufferingConfig.create();
30-
private Duration sessionTimeout = Duration.ofMinutes(15);
30+
private SessionConfig sessionConfig = SessionConfig.withDefaults();
3131
private final List<String> suppressedInstrumentations = new ArrayList<>();
3232

3333
/**
@@ -132,15 +132,20 @@ public OtelRumConfig setDiskBufferingConfig(DiskBufferingConfig diskBufferingCon
132132
return this;
133133
}
134134

135-
/** Call this method to set session timeout in minutes */
136-
public OtelRumConfig setSessionTimeout(Duration sessionTimeout) {
137-
this.sessionTimeout = sessionTimeout;
135+
/**
136+
* Sets the session configuration, which includes inactivity timeout and maximum lifetime
137+
* durations.
138+
*
139+
* @return this
140+
*/
141+
public OtelRumConfig setSessionConfig(SessionConfig sessionConfig) {
142+
this.sessionConfig = sessionConfig;
138143
return this;
139144
}
140145

141-
/** Call this method to retrieve session timeout */
142-
public Duration getSessionTimeout() {
143-
return sessionTimeout;
146+
/** Call this method to retrieve the session config */
147+
public SessionConfig getSessionConfig() {
148+
return sessionConfig;
144149
}
145150

146151
/**

core/src/main/java/io/opentelemetry/android/internal/session/SessionIdTimeoutHandler.kt

+6-11
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
package io.opentelemetry.android.internal.session
77

88
import io.opentelemetry.android.internal.services.applifecycle.ApplicationStateListener
9+
import io.opentelemetry.android.session.SessionConfig
910
import io.opentelemetry.sdk.common.Clock
10-
import java.time.Duration
11+
import kotlin.time.Duration
1112

1213
/**
1314
* This class encapsulates the following criteria about the sessionId timeout:
@@ -25,7 +26,7 @@ import java.time.Duration
2526
*/
2627
internal class SessionIdTimeoutHandler(
2728
private val clock: Clock,
28-
private val sessionTimeout: Duration,
29+
private val sessionBackgroundInactivityTimeout: Duration,
2930
) : ApplicationStateListener {
3031
@Volatile
3132
private var timeoutStartNanos: Long = 0
@@ -34,10 +35,9 @@ internal class SessionIdTimeoutHandler(
3435
private var state = State.FOREGROUND
3536

3637
// for testing
37-
@JvmOverloads
38-
internal constructor(sessionTimeout: Duration = DEFAULT_SESSION_TIMEOUT) : this(
38+
internal constructor(sessionConfig: SessionConfig) : this(
3939
Clock.getDefault(),
40-
sessionTimeout,
40+
sessionConfig.backgroundInactivityTimeout,
4141
)
4242

4343
override fun onApplicationForegrounded() {
@@ -54,7 +54,7 @@ internal class SessionIdTimeoutHandler(
5454
return false
5555
}
5656
val elapsedTime = clock.nanoTime() - timeoutStartNanos
57-
return elapsedTime >= sessionTimeout.toNanos()
57+
return elapsedTime >= sessionBackgroundInactivityTimeout.inWholeNanoseconds
5858
}
5959

6060
fun bump() {
@@ -73,9 +73,4 @@ internal class SessionIdTimeoutHandler(
7373
/** A temporary state representing the first event after the app has been brought back. */
7474
TRANSITIONING_TO_FOREGROUND,
7575
}
76-
77-
companion object {
78-
@JvmField
79-
val DEFAULT_SESSION_TIMEOUT: Duration = Duration.ofMinutes(15)
80-
}
8176
}

core/src/main/java/io/opentelemetry/android/internal/session/SessionManagerImpl.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,21 @@
66
package io.opentelemetry.android.internal.session
77

88
import io.opentelemetry.android.session.Session
9+
import io.opentelemetry.android.session.SessionConfig
910
import io.opentelemetry.android.session.SessionIdGenerator
1011
import io.opentelemetry.android.session.SessionManager
1112
import io.opentelemetry.android.session.SessionObserver
1213
import io.opentelemetry.android.session.SessionStorage
1314
import io.opentelemetry.sdk.common.Clock
1415
import java.util.Collections.synchronizedList
15-
import java.util.concurrent.TimeUnit
16+
import kotlin.time.Duration
1617

1718
internal class SessionManagerImpl(
1819
private val clock: Clock = Clock.getDefault(),
1920
private val sessionStorage: SessionStorage = SessionStorage.InMemory(),
2021
private val timeoutHandler: SessionIdTimeoutHandler,
2122
private val idGenerator: SessionIdGenerator = SessionIdGenerator.DEFAULT,
22-
private val sessionLifetimeNanos: Long = TimeUnit.HOURS.toNanos(4),
23+
private val maxSessionLifetime: Duration,
2324
) : SessionManager {
2425
// TODO: Make thread safe / wrap with AtomicReference?
2526
private var session: Session = Session.NONE
@@ -66,18 +67,18 @@ internal class SessionManagerImpl(
6667

6768
private fun sessionHasExpired(): Boolean {
6869
val elapsedTime = clock.now() - session.getStartTimestamp()
69-
return elapsedTime >= sessionLifetimeNanos
70+
return elapsedTime >= maxSessionLifetime.inWholeNanoseconds
7071
}
7172

7273
companion object {
7374
@JvmStatic
7475
fun create(
7576
timeoutHandler: SessionIdTimeoutHandler,
76-
sessionLifetimeNanos: Long,
77+
sessionConfig: SessionConfig,
7778
): SessionManagerImpl =
7879
SessionManagerImpl(
7980
timeoutHandler = timeoutHandler,
80-
sessionLifetimeNanos = sessionLifetimeNanos,
81+
maxSessionLifetime = sessionConfig.maxLifetime,
8182
)
8283
}
8384
}

core/src/test/java/io/opentelemetry/android/internal/session/SessionIdTimeoutHandlerTest.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55

66
package io.opentelemetry.android.internal.session
77

8-
import io.opentelemetry.android.internal.session.SessionIdTimeoutHandler.Companion.DEFAULT_SESSION_TIMEOUT
8+
import io.opentelemetry.android.session.SessionConfig
99
import io.opentelemetry.sdk.testing.time.TestClock
1010
import org.junit.jupiter.api.Assertions.assertFalse
1111
import org.junit.jupiter.api.Assertions.assertTrue
1212
import org.junit.jupiter.api.Test
1313
import java.time.Duration
1414
import java.util.concurrent.TimeUnit
15+
import kotlin.time.Duration.Companion.nanoseconds
1516

1617
class SessionIdTimeoutHandlerTest {
1718
@Test
1819
fun shouldNeverTimeOutInForeground() {
1920
val clock: TestClock = TestClock.create()
2021
val timeoutHandler =
21-
SessionIdTimeoutHandler(clock, DEFAULT_SESSION_TIMEOUT)
22+
SessionIdTimeoutHandler(clock, SessionConfig.withDefaults().backgroundInactivityTimeout)
2223

2324
assertFalse(timeoutHandler.hasTimedOut())
2425
timeoutHandler.bump()
@@ -32,7 +33,7 @@ class SessionIdTimeoutHandlerTest {
3233
fun shouldApply15MinutesTimeoutToAppsInBackground() {
3334
val clock: TestClock = TestClock.create()
3435
val timeoutHandler =
35-
SessionIdTimeoutHandler(clock, DEFAULT_SESSION_TIMEOUT)
36+
SessionIdTimeoutHandler(clock, SessionConfig.withDefaults().backgroundInactivityTimeout)
3637

3738
timeoutHandler.onApplicationBackgrounded()
3839
timeoutHandler.bump()
@@ -64,7 +65,7 @@ class SessionIdTimeoutHandlerTest {
6465
fun shouldApplyTimeoutToFirstSpanAfterAppBeingMovedToForeground() {
6566
val clock: TestClock = TestClock.create()
6667
val timeoutHandler =
67-
SessionIdTimeoutHandler(clock, DEFAULT_SESSION_TIMEOUT)
68+
SessionIdTimeoutHandler(clock, SessionConfig.withDefaults().backgroundInactivityTimeout)
6869

6970
timeoutHandler.onApplicationBackgrounded()
7071
timeoutHandler.bump()
@@ -84,7 +85,7 @@ class SessionIdTimeoutHandlerTest {
8485
fun shouldApplyCustomTimeoutToFirstSpanAfterAppBeingMovedToForeground() {
8586
val clock: TestClock = TestClock.create()
8687
val timeoutHandler =
87-
SessionIdTimeoutHandler(clock, Duration.ofNanos(5))
88+
SessionIdTimeoutHandler(clock, 5.nanoseconds)
8889

8990
timeoutHandler.onApplicationBackgrounded()
9091
timeoutHandler.bump()

core/src/test/java/io/opentelemetry/android/internal/session/SessionManagerImplTest.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.BeforeEach
2222
import org.junit.jupiter.api.Test
2323
import java.util.concurrent.TimeUnit
2424
import java.util.regex.Pattern
25+
import kotlin.time.Duration.Companion.hours
2526

2627
internal class SessionManagerImplTest {
2728
@MockK
@@ -40,6 +41,7 @@ internal class SessionManagerImplTest {
4041
SessionManagerImpl(
4142
TestClock.create(),
4243
timeoutHandler = timeoutHandler,
44+
maxSessionLifetime = 4.hours,
4345
)
4446
val sessionId = sessionManager.getSessionId()
4547
assertThat(sessionId).isNotNull()
@@ -54,6 +56,7 @@ internal class SessionManagerImplTest {
5456
SessionManagerImpl(
5557
clock,
5658
timeoutHandler = timeoutHandler,
59+
maxSessionLifetime = 4.hours,
5760
)
5861
val value = sessionManager.getSessionId()
5962
assertThat(value).isEqualTo(sessionManager.getSessionId())
@@ -82,6 +85,7 @@ internal class SessionManagerImplTest {
8285
SessionManagerImpl(
8386
clock,
8487
timeoutHandler = timeoutHandler,
88+
maxSessionLifetime = 4.hours,
8589
)
8690
sessionManager.addObserver(observer)
8791

@@ -121,7 +125,7 @@ internal class SessionManagerImplTest {
121125

122126
@Test
123127
fun shouldCreateNewSessionIdAfterTimeout() {
124-
val sessionId = SessionManagerImpl(timeoutHandler = timeoutHandler)
128+
val sessionId = SessionManagerImpl(timeoutHandler = timeoutHandler, maxSessionLifetime = 4.hours)
125129

126130
val value = sessionId.getSessionId()
127131
verify { timeoutHandler.bump() }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.android.session
7+
8+
import kotlin.time.Duration
9+
import kotlin.time.Duration.Companion.hours
10+
import kotlin.time.Duration.Companion.minutes
11+
12+
data class SessionConfig(
13+
val backgroundInactivityTimeout: Duration = 15.minutes,
14+
val maxLifetime: Duration = 4.hours,
15+
) {
16+
companion object {
17+
@JvmStatic
18+
fun withDefaults(): SessionConfig = SessionConfig()
19+
}
20+
}

0 commit comments

Comments
 (0)