Skip to content

Commit

Permalink
Refactor session manager (#717)
Browse files Browse the repository at this point in the history
* factor out interface

* move session api to its own top module. make implementations internal.

* spotless

* use impl, fix compile
  • Loading branch information
breedx-splk authored Jan 10, 2025
1 parent fdc48d6 commit 7036754
Show file tree
Hide file tree
Showing 19 changed files with 263 additions and 206 deletions.
3 changes: 2 additions & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ android {

dependencies {
implementation(project(":instrumentation:android-instrumentation"))
implementation(project(":services"))
implementation(project(":common"))
implementation(project(":services"))
implementation(project(":session"))

implementation(libs.androidx.core)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import io.opentelemetry.android.internal.services.ServiceManager;
import io.opentelemetry.android.internal.services.ServiceManagerImpl;
import io.opentelemetry.android.internal.services.periodicwork.PeriodicWorkService;
import io.opentelemetry.android.internal.session.SessionIdTimeoutHandler;
import io.opentelemetry.android.internal.session.SessionManagerImpl;
import io.opentelemetry.android.session.SessionManager;
import io.opentelemetry.android.session.SessionProvider;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
Expand Down Expand Up @@ -302,7 +304,7 @@ public OpenTelemetryRum build() {
new BufferDelegatingSpanExporter();

SessionManager sessionManager =
SessionManager.create(timeoutHandler, config.getSessionTimeout().toNanos());
SessionManagerImpl.create(timeoutHandler, config.getSessionTimeout().toNanos());

OpenTelemetrySdk sdk =
OpenTelemetrySdk.builder()
Expand Down Expand Up @@ -373,6 +375,8 @@ private void initializeExporters(
}
initializationEvents.spanExporterInitialized(spanExporter);

SessionManager sessionManager =
SessionManagerImpl.create(timeoutHandler, config.getSessionTimeout().toNanos());
bufferedDelegatingLogExporter.setDelegate(logsExporter);

bufferDelegatingSpanExporter.setDelegate(spanExporter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import io.opentelemetry.android.instrumentation.AndroidInstrumentation
import io.opentelemetry.android.instrumentation.AndroidInstrumentationLoader
import io.opentelemetry.android.instrumentation.InstallationContext
import io.opentelemetry.android.internal.services.ServiceManager
import io.opentelemetry.android.internal.session.SessionIdTimeoutHandler
import io.opentelemetry.android.internal.session.SessionManagerImpl
import io.opentelemetry.android.session.SessionManager
import io.opentelemetry.sdk.OpenTelemetrySdk

Expand All @@ -19,7 +21,7 @@ class SdkPreconfiguredRumBuilder
private val application: Application,
private val sdk: OpenTelemetrySdk,
private val timeoutHandler: SessionIdTimeoutHandler = SessionIdTimeoutHandler(),
private val sessionManager: SessionManager = SessionManager(timeoutHandler = timeoutHandler),
private val sessionManager: SessionManager = SessionManagerImpl(timeoutHandler = timeoutHandler),
private val discoverInstrumentations: Boolean,
private val serviceManager: ServiceManager,
) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android.internal.session

import io.opentelemetry.android.internal.services.applifecycle.ApplicationStateListener
import io.opentelemetry.sdk.common.Clock
import java.time.Duration

/**
* This class encapsulates the following criteria about the sessionId timeout:
*
*
* * If the app is in the foreground sessionId should never time out.
* * If the app is in the background and no activity (spans) happens for >15 minutes, sessionId
* should time out.
* * If the app is in the background and some activity (spans) happens in <15 minute intervals,
* sessionId should not time out.
*
*
* Consequently, when the app spent >15 minutes without any activity (spans) in the background,
* after moving to the foreground the first span should trigger the sessionId timeout.
*/
internal class SessionIdTimeoutHandler(
private val clock: Clock,
private val sessionTimeout: Duration,
) : ApplicationStateListener {
@Volatile
private var timeoutStartNanos: Long = 0

@Volatile
private var state = State.FOREGROUND

// for testing
@JvmOverloads
internal constructor(sessionTimeout: Duration = DEFAULT_SESSION_TIMEOUT) : this(
Clock.getDefault(),
sessionTimeout,
)

override fun onApplicationForegrounded() {
state = State.TRANSITIONING_TO_FOREGROUND
}

override fun onApplicationBackgrounded() {
state = State.BACKGROUND
}

fun hasTimedOut(): Boolean {
// don't apply sessionId timeout to apps in the foreground
if (state == State.FOREGROUND) {
return false
}
val elapsedTime = clock.nanoTime() - timeoutStartNanos
return elapsedTime >= sessionTimeout.toNanos()
}

fun bump() {
timeoutStartNanos = clock.nanoTime()

// move from the temporary transition state to foreground after the first span
if (state == State.TRANSITIONING_TO_FOREGROUND) {
state = State.FOREGROUND
}
}

private enum class State {
FOREGROUND,
BACKGROUND,

/** A temporary state representing the first event after the app has been brought back. */
TRANSITIONING_TO_FOREGROUND,
}

companion object {
@JvmField
val DEFAULT_SESSION_TIMEOUT: Duration = Duration.ofMinutes(15)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android.session
package io.opentelemetry.android.internal.session

import io.opentelemetry.android.SessionIdTimeoutHandler
import io.opentelemetry.android.session.Session
import io.opentelemetry.android.session.SessionIdGenerator
import io.opentelemetry.android.session.SessionManager
import io.opentelemetry.android.session.SessionObserver
import io.opentelemetry.android.session.SessionStorage
import io.opentelemetry.sdk.common.Clock
import java.util.Collections.synchronizedList
import java.util.concurrent.TimeUnit

internal class SessionManager(
internal class SessionManagerImpl(
private val clock: Clock = Clock.getDefault(),
private val sessionStorage: SessionStorage = SessionStorage.InMemory(),
private val timeoutHandler: SessionIdTimeoutHandler,
private val idGenerator: SessionIdGenerator = SessionIdGenerator.DEFAULT,
private val sessionLifetimeNanos: Long = TimeUnit.HOURS.toNanos(4),
) : SessionProvider,
SessionPublisher {
) : SessionManager {
// TODO: Make thread safe / wrap with AtomicReference?
private var session: Session = Session.NONE
private val observers = synchronizedList(ArrayList<SessionObserver>())
Expand Down Expand Up @@ -70,8 +73,8 @@ internal class SessionManager(
fun create(
timeoutHandler: SessionIdTimeoutHandler,
sessionLifetimeNanos: Long,
): SessionManager =
SessionManager(
): SessionManagerImpl =
SessionManagerImpl(
timeoutHandler = timeoutHandler,
sessionLifetimeNanos = sessionLifetimeNanos,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import io.opentelemetry.android.internal.services.applifecycle.AppLifecycleService;
import io.opentelemetry.android.internal.services.applifecycle.ApplicationStateListener;
import io.opentelemetry.android.internal.services.visiblescreen.VisibleScreenService;
import io.opentelemetry.android.internal.session.SessionIdTimeoutHandler;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.KeyValue;
import io.opentelemetry.api.common.Value;
Expand Down

This file was deleted.

Loading

0 comments on commit 7036754

Please sign in to comment.