diff --git a/android-agent/build.gradle.kts b/android-agent/build.gradle.kts index a162286fe..9e9f9fdb2 100644 --- a/android-agent/build.gradle.kts +++ b/android-agent/build.gradle.kts @@ -9,7 +9,8 @@ android { dependencies { api(project(":core")) - implementation(libs.opentelemetry.instrumentation.api) + implementation(libs.opentelemetry.sdk) + implementation(libs.opentelemetry.exporter.otlp) // Default instrumentations: api(project(":instrumentation:activity")) diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt new file mode 100644 index 000000000..34929e2dc --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt @@ -0,0 +1,167 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.agent + +import android.app.Application +import io.opentelemetry.android.OpenTelemetryRum +import io.opentelemetry.android.OpenTelemetryRumBuilder +import io.opentelemetry.android.agent.endpoint.EndpointConfig +import io.opentelemetry.android.agent.session.SessionIdTimeoutHandler +import io.opentelemetry.android.agent.session.SessionManager +import io.opentelemetry.android.config.OtelRumConfig +import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfiguration +import io.opentelemetry.android.instrumentation.AndroidInstrumentationLoader +import io.opentelemetry.android.instrumentation.activity.ActivityLifecycleInstrumentation +import io.opentelemetry.android.instrumentation.anr.AnrInstrumentation +import io.opentelemetry.android.instrumentation.common.ScreenNameExtractor +import io.opentelemetry.android.instrumentation.crash.CrashDetails +import io.opentelemetry.android.instrumentation.crash.CrashReporterInstrumentation +import io.opentelemetry.android.instrumentation.fragment.FragmentLifecycleInstrumentation +import io.opentelemetry.android.instrumentation.network.NetworkChangeInstrumentation +import io.opentelemetry.android.instrumentation.slowrendering.SlowRenderingInstrumentation +import io.opentelemetry.android.internal.services.ServiceManager +import io.opentelemetry.android.internal.services.network.data.CurrentNetwork +import io.opentelemetry.api.trace.Tracer +import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor +import io.opentelemetry.sdk.common.Clock +import java.time.Duration + +object AndroidAgent { + private val activityLifecycleInstrumentation by lazy { + AndroidInstrumentationLoader.getInstrumentation( + ActivityLifecycleInstrumentation::class.java, + )!! + } + private val fragmentLifecycleInstrumentation by lazy { + AndroidInstrumentationLoader.getInstrumentation( + FragmentLifecycleInstrumentation::class.java, + )!! + } + private val anrInstrumentation by lazy { + AndroidInstrumentationLoader.getInstrumentation( + AnrInstrumentation::class.java, + )!! + } + private val crashReporterInstrumentation by lazy { + AndroidInstrumentationLoader.getInstrumentation( + CrashReporterInstrumentation::class.java, + )!! + } + private val networkChangeInstrumentation by lazy { + AndroidInstrumentationLoader.getInstrumentation(NetworkChangeInstrumentation::class.java)!! + } + private val slowRenderingInstrumentation by lazy { + AndroidInstrumentationLoader.getInstrumentation(SlowRenderingInstrumentation::class.java)!! + } + + fun createRumBuilder( + application: Application, + otelRumConfig: OtelRumConfig = OtelRumConfig(), + endpointConfig: EndpointConfig = EndpointConfig.getDefault("http://localhost"), + sessionTimeout: Duration = SessionIdTimeoutHandler.DEFAULT_SESSION_TIMEOUT, + activityTracerCustomizer: ((Tracer) -> Tracer)? = null, + activityNameExtractor: ScreenNameExtractor? = null, + fragmentTracerCustomizer: ((Tracer) -> Tracer)? = null, + fragmentNameExtractor: ScreenNameExtractor? = null, + anrAttributesExtractor: AttributesExtractor, Void>? = null, + crashAttributesExtractor: AttributesExtractor? = null, + networkChangeAttributesExtractor: AttributesExtractor? = null, + slowRenderingDetectionPollInterval: Duration? = null, + ): OpenTelemetryRumBuilder { + val rumBuilder = OpenTelemetryRum.builder(application, otelRumConfig) + + configureSessionProvider(rumBuilder, sessionTimeout) + configureExporters(rumBuilder, endpointConfig) + configureDiskBuffering(rumBuilder) + + applyInstrumentationConfigs( + activityTracerCustomizer, + activityNameExtractor, + fragmentTracerCustomizer, + fragmentNameExtractor, + anrAttributesExtractor, + crashAttributesExtractor, + networkChangeAttributesExtractor, + slowRenderingDetectionPollInterval, + ) + + return rumBuilder + } + + private fun configureSessionProvider( + rumBuilder: OpenTelemetryRumBuilder, + sessionTimeout: Duration, + ) { + val clock = Clock.getDefault() + val sessionIdTimeoutHandler = SessionIdTimeoutHandler(clock, sessionTimeout) + rumBuilder.setSessionProvider(SessionManager.create(clock, sessionIdTimeoutHandler)) + rumBuilder.addOtelSdkReadyListener { + ServiceManager.get().getAppLifecycleService().registerListener(sessionIdTimeoutHandler) + } + } + + private fun configureExporters( + rumBuilder: OpenTelemetryRumBuilder, + endpointConfig: EndpointConfig, + ) { + // Creating span exporter builder + val spanExporterBuilder = + OtlpHttpSpanExporter.builder().setEndpoint(endpointConfig.getSpanExporterUrl()) + // Creating log exporter builder + val logRecordExporterBuilder = + OtlpHttpLogRecordExporter.builder() + .setEndpoint(endpointConfig.getLogRecordExporterUrl()) + + // Adding headers + endpointConfig.getHeaders() + .forEach { (key, value) -> + spanExporterBuilder.addHeader(key, value) + logRecordExporterBuilder.addHeader(key, value) + } + + // Adding exporters to the rum builder + rumBuilder.setSpanExporter(spanExporterBuilder.build()) + rumBuilder.setLogRecordExporter(logRecordExporterBuilder.build()) + } + + private fun configureDiskBuffering(rumBuilder: OpenTelemetryRumBuilder) { + rumBuilder.setDiskBufferingConfiguration( + DiskBufferingConfiguration.builder() + .setEnabled(true) + .setMaxCacheSize(10_000_000).build(), + ) + } + + private fun applyInstrumentationConfigs( + activityTracerCustomizer: ((Tracer) -> Tracer)?, + activityNameExtractor: ScreenNameExtractor?, + fragmentTracerCustomizer: ((Tracer) -> Tracer)?, + fragmentNameExtractor: ScreenNameExtractor?, + anrAttributesExtractor: AttributesExtractor, Void>?, + crashAttributesExtractor: AttributesExtractor?, + networkChangeAttributesExtractor: AttributesExtractor?, + slowRenderingDetectionPollInterval: Duration?, + ) { + activityTracerCustomizer?.let { activityLifecycleInstrumentation.setTracerCustomizer(it) } + activityNameExtractor?.let { activityLifecycleInstrumentation.setScreenNameExtractor(it) } + fragmentTracerCustomizer?.let { fragmentLifecycleInstrumentation.setTracerCustomizer(it) } + fragmentNameExtractor?.let { fragmentLifecycleInstrumentation.setScreenNameExtractor(it) } + anrAttributesExtractor?.let { anrInstrumentation.addAttributesExtractor(it) } + crashAttributesExtractor?.let { crashReporterInstrumentation.addAttributesExtractor(it) } + networkChangeAttributesExtractor?.let { + networkChangeInstrumentation.addAttributesExtractor( + it, + ) + } + slowRenderingDetectionPollInterval?.let { + slowRenderingInstrumentation.setSlowRenderingDetectionPollInterval( + it, + ) + } + } +} diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/OtelRumConfigExtensions.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/OtelRumConfigExtensions.kt deleted file mode 100644 index c3a011208..000000000 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/OtelRumConfigExtensions.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.android.agent - -import io.opentelemetry.android.config.OtelRumConfig -import io.opentelemetry.android.instrumentation.AndroidInstrumentationLoader -import io.opentelemetry.android.instrumentation.activity.ActivityLifecycleInstrumentation -import io.opentelemetry.android.instrumentation.anr.AnrInstrumentation -import io.opentelemetry.android.instrumentation.common.ScreenNameExtractor -import io.opentelemetry.android.instrumentation.crash.CrashDetails -import io.opentelemetry.android.instrumentation.crash.CrashReporterInstrumentation -import io.opentelemetry.android.instrumentation.fragment.FragmentLifecycleInstrumentation -import io.opentelemetry.android.instrumentation.network.NetworkChangeInstrumentation -import io.opentelemetry.android.instrumentation.slowrendering.SlowRenderingInstrumentation -import io.opentelemetry.android.internal.services.network.data.CurrentNetwork -import io.opentelemetry.api.trace.Tracer -import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor -import java.time.Duration - -/** - * Convenience functions to allow configuring the default instrumentations through the [OtelRumConfig] object, for example: - * - * ``` - * OtelRumConfig() - * .setSessionTimeout(Duration.ofSeconds(10)) // Real OtelRumConfig function - * .setSlowRenderingDetectionPollInterval(Duration.ofSeconds(5)) // Extension function - * .disableScreenAttributes() // Real OtelRumConfig function - * ``` - */ - -fun OtelRumConfig.setActivityTracerCustomizer(customizer: (Tracer) -> Tracer): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(ActivityLifecycleInstrumentation::class.java) - ?.setTracerCustomizer(customizer) - return this -} - -fun OtelRumConfig.setActivityNameExtractor(screenNameExtractor: ScreenNameExtractor): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(ActivityLifecycleInstrumentation::class.java) - ?.setScreenNameExtractor(screenNameExtractor) - return this -} - -fun OtelRumConfig.setFragmentTracerCustomizer(customizer: (Tracer) -> Tracer): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(FragmentLifecycleInstrumentation::class.java) - ?.setTracerCustomizer(customizer) - return this -} - -fun OtelRumConfig.setFragmentNameExtractor(screenNameExtractor: ScreenNameExtractor): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(FragmentLifecycleInstrumentation::class.java) - ?.setScreenNameExtractor(screenNameExtractor) - return this -} - -fun OtelRumConfig.addAnrAttributesExtractor(extractor: AttributesExtractor, Void>): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(AnrInstrumentation::class.java) - ?.addAttributesExtractor(extractor) - return this -} - -fun OtelRumConfig.addCrashAttributesExtractor(extractor: AttributesExtractor): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(CrashReporterInstrumentation::class.java) - ?.addAttributesExtractor(extractor) - return this -} - -fun OtelRumConfig.addNetworkChangeAttributesExtractor(extractor: AttributesExtractor): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(NetworkChangeInstrumentation::class.java) - ?.addAttributesExtractor(extractor) - return this -} - -fun OtelRumConfig.setSlowRenderingDetectionPollInterval(interval: Duration): OtelRumConfig { - AndroidInstrumentationLoader.getInstrumentation(SlowRenderingInstrumentation::class.java) - ?.setSlowRenderingDetectionPollInterval(interval) - return this -} diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultHttpEndpointConfig.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultHttpEndpointConfig.kt new file mode 100644 index 000000000..9f03eea6e --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultHttpEndpointConfig.kt @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.agent.endpoint + +internal data class DefaultHttpEndpointConfig( + private val baseUrl: String, + private val headers: Map, +) : EndpointConfig { + override fun getSpanExporterUrl(): String { + return "$baseUrl/v1/traces" + } + + override fun getLogRecordExporterUrl(): String { + return "$baseUrl/v1/logs" + } + + override fun getHeaders(): Map { + return headers + } +} diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/EndpointConfig.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/EndpointConfig.kt new file mode 100644 index 000000000..8cc35170c --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/EndpointConfig.kt @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.agent.endpoint + +interface EndpointConfig { + fun getSpanExporterUrl(): String + + fun getLogRecordExporterUrl(): String + + fun getHeaders(): Map + + companion object { + @JvmStatic + fun getDefault( + baseUrl: String, + headers: Map = emptyMap(), + ): EndpointConfig { + return DefaultHttpEndpointConfig(baseUrl.trimEnd('/'), headers) + } + } +} diff --git a/core/src/main/java/io/opentelemetry/android/session/Session.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/Session.kt similarity index 94% rename from core/src/main/java/io/opentelemetry/android/session/Session.kt rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/Session.kt index 144976f68..519030798 100644 --- a/core/src/main/java/io/opentelemetry/android/session/Session.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/Session.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.android.session +package io.opentelemetry.android.agent.session interface Session { fun getId(): String diff --git a/core/src/main/java/io/opentelemetry/android/session/SessionIdGenerator.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdGenerator.kt similarity index 92% rename from core/src/main/java/io/opentelemetry/android/session/SessionIdGenerator.kt rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdGenerator.kt index 94a5c6fe2..19e070315 100644 --- a/core/src/main/java/io/opentelemetry/android/session/SessionIdGenerator.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdGenerator.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.android.session +package io.opentelemetry.android.agent.session import io.opentelemetry.api.trace.TraceId import java.util.Random diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.kt new file mode 100644 index 000000000..90c71213f --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.kt @@ -0,0 +1,70 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.agent.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 = Clock.getDefault(), + private val sessionTimeout: Duration = DEFAULT_SESSION_TIMEOUT, +) : ApplicationStateListener { + @Volatile + private var timeoutStartNanos: Long = 0 + + @Volatile + private var state = State.FOREGROUND + + 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 { + val DEFAULT_SESSION_TIMEOUT: Duration = Duration.ofMinutes(15) + } +} diff --git a/core/src/main/java/io/opentelemetry/android/session/SessionManager.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionManager.kt similarity index 88% rename from core/src/main/java/io/opentelemetry/android/session/SessionManager.kt rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionManager.kt index 7608da1b1..3f6539b87 100644 --- a/core/src/main/java/io/opentelemetry/android/session/SessionManager.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionManager.kt @@ -3,19 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.android.session +package io.opentelemetry.android.agent.session -import io.opentelemetry.android.SessionIdTimeoutHandler +import io.opentelemetry.android.session.SessionProvider import io.opentelemetry.sdk.common.Clock import java.util.Collections.synchronizedList import java.util.concurrent.TimeUnit internal class SessionManager( - private val clock: Clock = Clock.getDefault(), - private val sessionStorage: SessionStorage = SessionStorage.InMemory(), private val timeoutHandler: SessionIdTimeoutHandler, + private val clock: Clock, + private val sessionLifetimeNanos: Long, + private val sessionStorage: SessionStorage = SessionStorage.InMemory(), private val idGenerator: SessionIdGenerator = SessionIdGenerator.DEFAULT, - private val sessionLifetimeNanos: Long = TimeUnit.HOURS.toNanos(4), ) : SessionProvider, SessionPublisher { // TODO: Make thread safe / wrap with AtomicReference? private var session: Session = Session.NONE @@ -67,12 +67,14 @@ internal class SessionManager( companion object { @JvmStatic fun create( + clock: Clock, timeoutHandler: SessionIdTimeoutHandler, - sessionLifetimeNanos: Long, + sessionLifetimeNanos: Long = TimeUnit.HOURS.toNanos(4), ): SessionManager { return SessionManager( timeoutHandler = timeoutHandler, sessionLifetimeNanos = sessionLifetimeNanos, + clock = clock, ) } } diff --git a/core/src/main/java/io/opentelemetry/android/session/SessionObserver.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionObserver.kt similarity index 84% rename from core/src/main/java/io/opentelemetry/android/session/SessionObserver.kt rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionObserver.kt index 35cccfe4b..dea4943a6 100644 --- a/core/src/main/java/io/opentelemetry/android/session/SessionObserver.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionObserver.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.android.session +package io.opentelemetry.android.agent.session interface SessionObserver { fun onSessionStarted( diff --git a/core/src/main/java/io/opentelemetry/android/session/SessionPublisher.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionPublisher.kt similarity index 77% rename from core/src/main/java/io/opentelemetry/android/session/SessionPublisher.kt rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionPublisher.kt index b96c90a93..f86e26ed6 100644 --- a/core/src/main/java/io/opentelemetry/android/session/SessionPublisher.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionPublisher.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.android.session +package io.opentelemetry.android.agent.session interface SessionPublisher { fun addObserver(observer: SessionObserver) diff --git a/core/src/main/java/io/opentelemetry/android/session/SessionStorage.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionStorage.kt similarity index 90% rename from core/src/main/java/io/opentelemetry/android/session/SessionStorage.kt rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionStorage.kt index 3d6aa1de4..519f22fa9 100644 --- a/core/src/main/java/io/opentelemetry/android/session/SessionStorage.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionStorage.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.android.session +package io.opentelemetry.android.agent.session interface SessionStorage { fun get(): Session diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6d32acda9..6ffe2f0b5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -68,10 +68,9 @@ dependencies { api(platform(libs.opentelemetry.platform)) api(libs.opentelemetry.api) + api(libs.opentelemetry.instrumentation.api) implementation(libs.opentelemetry.sdk) implementation(libs.opentelemetry.api.incubator) - implementation(libs.opentelemetry.exporter.logging) - implementation(libs.opentelemetry.instrumentation.api) implementation(libs.opentelemetry.semconv.incubating) implementation(libs.opentelemetry.diskBuffering) testImplementation(libs.opentelemetry.api.incubator) diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRum.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRum.java index 2810f8de5..acabc37bb 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRum.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRum.java @@ -8,11 +8,13 @@ import android.app.Application; import io.opentelemetry.android.config.OtelRumConfig; import io.opentelemetry.android.internal.services.ServiceManager; +import io.opentelemetry.android.session.SessionProvider; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.trace.SdkTracerProvider; +import javax.annotation.Nullable; /** * Entrypoint for the OpenTelemetry Real User Monitoring library for Android. @@ -65,10 +67,15 @@ static OpenTelemetryRumBuilder builder(Application application, OtelRumConfig co static SdkPreconfiguredRumBuilder builder( Application application, OpenTelemetrySdk openTelemetrySdk, + SessionProvider sessionProvider, boolean discoverInstrumentations) { ServiceManager.initialize(application); return new SdkPreconfiguredRumBuilder( - application, openTelemetrySdk, discoverInstrumentations, ServiceManager.get()); + application, + openTelemetrySdk, + sessionProvider, + discoverInstrumentations, + ServiceManager.get()); } /** Returns a no-op implementation of {@link OpenTelemetryRum}. */ @@ -87,5 +94,6 @@ static OpenTelemetryRum noop() { * Note: this value will change throughout the lifetime of an application instance, so it is * recommended that you do not cache this value, but always retrieve it from here when needed. */ + @Nullable String getRumSessionId(); } diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index 1b33bd022..6c64829f7 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -23,19 +23,19 @@ import io.opentelemetry.android.internal.services.CacheStorage; import io.opentelemetry.android.internal.services.Preferences; import io.opentelemetry.android.internal.services.ServiceManager; -import io.opentelemetry.android.session.SessionManager; import io.opentelemetry.android.session.SessionProvider; import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.contrib.disk.buffering.LogRecordFromDiskExporter; import io.opentelemetry.contrib.disk.buffering.LogRecordToDiskExporter; +import io.opentelemetry.contrib.disk.buffering.MetricFromDiskExporter; +import io.opentelemetry.contrib.disk.buffering.MetricToDiskExporter; import io.opentelemetry.contrib.disk.buffering.SpanFromDiskExporter; import io.opentelemetry.contrib.disk.buffering.SpanToDiskExporter; import io.opentelemetry.contrib.disk.buffering.StorageConfiguration; -import io.opentelemetry.exporter.logging.LoggingSpanExporter; -import io.opentelemetry.exporter.logging.SystemOutLogRecordExporter; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.logs.LogRecordProcessor; import io.opentelemetry.sdk.logs.SdkLoggerProvider; @@ -44,6 +44,8 @@ import io.opentelemetry.sdk.logs.export.LogRecordExporter; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; @@ -56,6 +58,7 @@ import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; import javax.annotation.Nullable; /** @@ -77,12 +80,15 @@ public final class OpenTelemetryRumBuilder { private final OtelRumConfig config; private final List instrumentations = new ArrayList<>(); private final List> otelSdkReadyListeners = new ArrayList<>(); - private final SessionIdTimeoutHandler timeoutHandler; - private Function spanExporterCustomizer = a -> a; - private Function - logRecordExporterCustomizer = a -> a; + private SessionProvider sessionProvider = SessionProvider.NO_OP; + @Nullable private SpanExporter spanExporter; + @Nullable private LogRecordExporter logRecordExporter; + @Nullable private MetricExporter metricExporter; private Function propagatorCustomizer = (a) -> a; + private Supplier globalAttributesSupplier = Attributes::empty; + private DiskBufferingConfiguration diskBufferingConfiguration = + DiskBufferingConfiguration.builder().build(); private Resource resource; @@ -92,14 +98,11 @@ private static TextMapPropagator buildDefaultPropagator() { } public static OpenTelemetryRumBuilder create(Application application, OtelRumConfig config) { - return new OpenTelemetryRumBuilder( - application, config, new SessionIdTimeoutHandler(config.getSessionTimeout())); + return new OpenTelemetryRumBuilder(application, config); } - OpenTelemetryRumBuilder( - Application application, OtelRumConfig config, SessionIdTimeoutHandler timeoutHandler) { + OpenTelemetryRumBuilder(Application application, OtelRumConfig config) { this.application = application; - this.timeoutHandler = timeoutHandler; this.resource = AndroidResource.createDefault(application); this.config = config; } @@ -115,6 +118,10 @@ public OpenTelemetryRumBuilder setResource(Resource resource) { return this; } + public void setSessionProvider(SessionProvider sessionProvider) { + this.sessionProvider = sessionProvider; + } + /** * Merges a new {@link Resource} with any existing {@link Resource} in this builder. The * resulting {@link Resource} will be attached to all telemetry emitted by the {@link @@ -127,6 +134,48 @@ public OpenTelemetryRumBuilder mergeResource(Resource resource) { return this; } + /** + * Configures the set of global attributes to emit with every span and event. Any existing + * configured attributes will be dropped. Default = none. + */ + public OpenTelemetryRumBuilder setGlobalAttributes(Attributes attributes) { + return setGlobalAttributes(() -> attributes); + } + + public OpenTelemetryRumBuilder setGlobalAttributes( + Supplier globalAttributesSupplier) { + this.globalAttributesSupplier = globalAttributesSupplier; + return this; + } + + private boolean hasGlobalAttributes() { + Attributes attributes = globalAttributesSupplier.get(); + return attributes != null && !attributes.isEmpty(); + } + + /** + * Sets the parameters for caching signals in disk in order to export them later. + * + * @return this + */ + public OpenTelemetryRumBuilder setDiskBufferingConfiguration( + DiskBufferingConfiguration diskBufferingConfiguration) { + this.diskBufferingConfiguration = diskBufferingConfiguration; + return this; + } + + public void setSpanExporter(SpanExporter spanExporter) { + this.spanExporter = spanExporter; + } + + public void setLogRecordExporter(LogRecordExporter logRecordExporter) { + this.logRecordExporter = logRecordExporter; + } + + public void setMetricExporter(MetricExporter metricExporter) { + this.metricExporter = metricExporter; + } + /** * Adds a {@link BiFunction} to invoke the with the {@link SdkTracerProviderBuilder} to allow * customization. The return value of the {@link BiFunction} will replace the passed-in @@ -217,44 +266,6 @@ public OpenTelemetryRumBuilder addPropagatorCustomizer( return this; } - /** - * Adds a {@link Function} to invoke with the default {@link SpanExporter} to allow - * customization. The return value of the {@link Function} will replace the passed-in argument. - * - *

Multiple calls will execute the customizers in order. - */ - public OpenTelemetryRumBuilder addSpanExporterCustomizer( - Function spanExporterCustomizer) { - requireNonNull(spanExporterCustomizer, "spanExporterCustomizer"); - Function existing = - this.spanExporterCustomizer; - this.spanExporterCustomizer = - exporter -> { - SpanExporter intermediate = existing.apply(exporter); - return spanExporterCustomizer.apply(intermediate); - }; - return this; - } - - /** - * Adds a {@link Function} to invoke with the default {@link LogRecordExporter} to allow - * customization. The return value of the {@link Function} will replace the passed-in argument. - * - *

Multiple calls will execute the customizers in order. - */ - public OpenTelemetryRumBuilder addLogRecordExporterCustomizer( - Function - logRecordExporterCustomizer) { - Function existing = - this.logRecordExporterCustomizer; - this.logRecordExporterCustomizer = - exporter -> { - LogRecordExporter intermediate = existing.apply(exporter); - return logRecordExporterCustomizer.apply(intermediate); - }; - return this; - } - /** * Creates a new instance of {@link OpenTelemetryRum} with the settings of this {@link * OpenTelemetryRumBuilder}. @@ -273,42 +284,49 @@ OpenTelemetryRum build(ServiceManager serviceManager) { InitializationEvents initializationEvents = InitializationEvents.get(); applyConfiguration(serviceManager, initializationEvents); - DiskBufferingConfiguration diskBufferingConfiguration = - config.getDiskBufferingConfiguration(); - SpanExporter spanExporter = buildSpanExporter(); - LogRecordExporter logsExporter = buildLogsExporter(); + SpanExporter spanExporter = this.spanExporter; + LogRecordExporter logsExporter = this.logRecordExporter; + MetricExporter metricExporter = this.metricExporter; SignalFromDiskExporter signalFromDiskExporter = null; if (diskBufferingConfiguration.isEnabled()) { try { StorageConfiguration storageConfiguration = createStorageConfiguration(serviceManager); final SpanExporter originalSpanExporter = spanExporter; - spanExporter = - SpanToDiskExporter.create(originalSpanExporter, storageConfiguration); + if (originalSpanExporter != null) { + spanExporter = + SpanToDiskExporter.create(originalSpanExporter, storageConfiguration); + } final LogRecordExporter originalLogsExporter = logsExporter; - logsExporter = - LogRecordToDiskExporter.create(originalLogsExporter, storageConfiguration); + if (originalLogsExporter != null) { + logsExporter = + LogRecordToDiskExporter.create( + originalLogsExporter, storageConfiguration); + } + final MetricExporter originalMetricExporter = metricExporter; + if (originalMetricExporter != null) { + metricExporter = + MetricToDiskExporter.create( + metricExporter, + storageConfiguration, + originalMetricExporter::getAggregationTemporality); + } signalFromDiskExporter = - new SignalFromDiskExporter( - SpanFromDiskExporter.create( - originalSpanExporter, storageConfiguration), - null, - LogRecordFromDiskExporter.create( - originalLogsExporter, storageConfiguration)); + getSignalFromDiskExporter( + storageConfiguration, + originalSpanExporter, + originalMetricExporter, + originalLogsExporter); } catch (IOException e) { Log.e(RumConstants.OTEL_RUM_LOG_TAG, "Could not initialize disk exporters.", e); } } initializationEvents.spanExporterInitialized(spanExporter); - SessionManager sessionManager = - SessionManager.create(timeoutHandler, config.getSessionTimeout().toNanos()); - OpenTelemetrySdk sdk = OpenTelemetrySdk.builder() - .setTracerProvider( - buildTracerProvider(sessionManager, application, spanExporter)) - .setMeterProvider(buildMeterProvider(application)) + .setTracerProvider(buildTracerProvider(application, spanExporter)) + .setMeterProvider(buildMeterProvider(application, metricExporter)) .setLoggerProvider(buildLoggerProvider(application, logsExporter)) .setPropagators(buildFinalPropagators()) .build(); @@ -321,27 +339,44 @@ OpenTelemetryRum build(ServiceManager serviceManager) { new SdkPreconfiguredRumBuilder( application, sdk, - timeoutHandler, - sessionManager, + sessionProvider, config.shouldDiscoverInstrumentations(), serviceManager); instrumentations.forEach(delegate::addInstrumentation); return delegate.build(); } + private static SignalFromDiskExporter getSignalFromDiskExporter( + StorageConfiguration storageConfiguration, + @Nullable SpanExporter spanExporter, + @Nullable MetricExporter metricExporter, + @Nullable LogRecordExporter logRecordExporter) + throws IOException { + return new SignalFromDiskExporter( + (spanExporter != null) + ? SpanFromDiskExporter.create(spanExporter, storageConfiguration) + : null, + (metricExporter != null) + ? MetricFromDiskExporter.create(metricExporter, storageConfiguration) + : null, + (logRecordExporter != null) + ? LogRecordFromDiskExporter.create(logRecordExporter, storageConfiguration) + : null); + } + private StorageConfiguration createStorageConfiguration(ServiceManager serviceManager) throws IOException { Preferences preferences = serviceManager.getPreferences(); CacheStorage storage = serviceManager.getCacheStorage(); - DiskBufferingConfiguration config = this.config.getDiskBufferingConfiguration(); - DiskManager diskManager = new DiskManager(storage, preferences, config); + DiskManager diskManager = new DiskManager(storage, preferences, diskBufferingConfiguration); return StorageConfiguration.builder() .setRootDir(diskManager.getSignalsBufferDir()) .setMaxFileSize(diskManager.getMaxCacheFileSize()) .setMaxFolderSize(diskManager.getMaxFolderSize()) - .setMaxFileAgeForWriteMillis(config.getMaxFileAgeForWriteMillis()) - .setMaxFileAgeForReadMillis(config.getMaxFileAgeForReadMillis()) - .setMinFileAgeForReadMillis(config.getMinFileAgeForReadMillis()) + .setMaxFileAgeForWriteMillis( + diskBufferingConfiguration.getMaxFileAgeForWriteMillis()) + .setMaxFileAgeForReadMillis(diskBufferingConfiguration.getMaxFileAgeForReadMillis()) + .setMinFileAgeForReadMillis(diskBufferingConfiguration.getMinFileAgeForReadMillis()) .setTemporaryFileProvider( new SimpleTemporaryFileProvider(diskManager.getTemporaryDir())) .build(); @@ -386,10 +421,10 @@ private void applyConfiguration( initializationEvents.sdkInitializationStarted(); // Global attributes - if (config.hasGlobalAttributes()) { + if (hasGlobalAttributes()) { // Add span processor that appends global attributes. GlobalAttributesSpanAppender appender = - GlobalAttributesSpanAppender.create(config.getGlobalAttributesSupplier()); + GlobalAttributesSpanAppender.create(globalAttributesSupplier); addTracerProviderCustomizer( (tracerProviderBuilder, app) -> tracerProviderBuilder.addSpanProcessor(appender)); @@ -422,14 +457,17 @@ private void applyConfiguration( } private SdkTracerProvider buildTracerProvider( - SessionProvider sessionProvider, Application application, SpanExporter spanExporter) { + Application application, @Nullable SpanExporter spanExporter) { SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder() .setResource(resource) .addSpanProcessor(new SessionIdSpanAppender(sessionProvider)); - BatchSpanProcessor batchSpanProcessor = BatchSpanProcessor.builder(spanExporter).build(); - tracerProviderBuilder.addSpanProcessor(batchSpanProcessor); + if (spanExporter != null) { + BatchSpanProcessor batchSpanProcessor = + BatchSpanProcessor.builder(spanExporter).build(); + tracerProviderBuilder.addSpanProcessor(batchSpanProcessor); + } for (BiFunction customizer : tracerProviderCustomizers) { @@ -439,16 +477,17 @@ private SdkTracerProvider buildTracerProvider( } private SdkLoggerProvider buildLoggerProvider( - Application application, LogRecordExporter logsExporter) { + Application application, @Nullable LogRecordExporter logsExporter) { SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder() .addLogRecordProcessor( - new GlobalAttributesLogRecordAppender( - config.getGlobalAttributesSupplier())) + new GlobalAttributesLogRecordAppender(globalAttributesSupplier)) .setResource(resource); - LogRecordProcessor batchLogsProcessor = - BatchLogRecordProcessor.builder(logsExporter).build(); - loggerProviderBuilder.addLogRecordProcessor(batchLogsProcessor); + if (logsExporter != null) { + LogRecordProcessor batchLogsProcessor = + BatchLogRecordProcessor.builder(logsExporter).build(); + loggerProviderBuilder.addLogRecordProcessor(batchLogsProcessor); + } for (BiFunction customizer : loggerProviderCustomizers) { loggerProviderBuilder = customizer.apply(loggerProviderBuilder, application); @@ -456,20 +495,13 @@ private SdkLoggerProvider buildLoggerProvider( return loggerProviderBuilder.build(); } - private SpanExporter buildSpanExporter() { - // TODO: Default to otlp...but how can we make endpoint and auth mandatory? - SpanExporter defaultExporter = LoggingSpanExporter.create(); - return spanExporterCustomizer.apply(defaultExporter); - } - - private LogRecordExporter buildLogsExporter() { - LogRecordExporter defaultExporter = SystemOutLogRecordExporter.create(); - return logRecordExporterCustomizer.apply(defaultExporter); - } - - private SdkMeterProvider buildMeterProvider(Application application) { + private SdkMeterProvider buildMeterProvider( + Application application, @Nullable MetricExporter metricExporter) { SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder().setResource(resource); + if (metricExporter != null) { + meterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); + } for (BiFunction customizer : meterProviderCustomizers) { meterProviderBuilder = customizer.apply(meterProviderBuilder, application); diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumImpl.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumImpl.java index e45afa1f9..d1db5e454 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumImpl.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumImpl.java @@ -5,18 +5,19 @@ package io.opentelemetry.android; -import io.opentelemetry.android.session.SessionManager; +import io.opentelemetry.android.session.SessionProvider; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.sdk.OpenTelemetrySdk; +import javax.annotation.Nullable; final class OpenTelemetryRumImpl implements OpenTelemetryRum { private final OpenTelemetrySdk openTelemetrySdk; - private final SessionManager sessionManager; + private final SessionProvider sessionProvider; - OpenTelemetryRumImpl(OpenTelemetrySdk openTelemetrySdk, SessionManager sessionManager) { + OpenTelemetryRumImpl(OpenTelemetrySdk openTelemetrySdk, SessionProvider sessionProvider) { this.openTelemetrySdk = openTelemetrySdk; - this.sessionManager = sessionManager; + this.sessionProvider = sessionProvider; } @Override @@ -25,7 +26,8 @@ public OpenTelemetry getOpenTelemetry() { } @Override + @Nullable public String getRumSessionId() { - return sessionManager.getSessionId(); + return sessionProvider.getSessionId(); } } diff --git a/core/src/main/java/io/opentelemetry/android/SdkPreconfiguredRumBuilder.kt b/core/src/main/java/io/opentelemetry/android/SdkPreconfiguredRumBuilder.kt index 0a5437aa8..be551c18f 100644 --- a/core/src/main/java/io/opentelemetry/android/SdkPreconfiguredRumBuilder.kt +++ b/core/src/main/java/io/opentelemetry/android/SdkPreconfiguredRumBuilder.kt @@ -9,7 +9,7 @@ import android.app.Application import io.opentelemetry.android.instrumentation.AndroidInstrumentation import io.opentelemetry.android.instrumentation.AndroidInstrumentationLoader import io.opentelemetry.android.internal.services.ServiceManager -import io.opentelemetry.android.session.SessionManager +import io.opentelemetry.android.session.SessionProvider import io.opentelemetry.sdk.OpenTelemetrySdk class SdkPreconfiguredRumBuilder @@ -17,13 +17,11 @@ class SdkPreconfiguredRumBuilder internal constructor( private val application: Application, private val sdk: OpenTelemetrySdk, - private val timeoutHandler: SessionIdTimeoutHandler = SessionIdTimeoutHandler(), - private val sessionManager: SessionManager = SessionManager(timeoutHandler = timeoutHandler), + private val sessionProvider: SessionProvider, private val discoverInstrumentations: Boolean, private val serviceManager: ServiceManager, ) { private val instrumentations = mutableListOf() - private val appLifecycleService by lazy { serviceManager.getAppLifecycleService() } /** * Adds an instrumentation to be applied as a part of the [build] method call. @@ -46,11 +44,8 @@ class SdkPreconfiguredRumBuilder */ fun build(): OpenTelemetryRum { serviceManager.start() - // the app state listeners need to be run in the first ActivityLifecycleCallbacks since they - // might turn off/on additional telemetry depending on whether the app is active or not - appLifecycleService.registerListener(timeoutHandler) - val openTelemetryRum = OpenTelemetryRumImpl(sdk, sessionManager) + val openTelemetryRum = OpenTelemetryRumImpl(sdk, sessionProvider) // Install instrumentations for (instrumentation in getInstrumentations()) { diff --git a/core/src/main/java/io/opentelemetry/android/SessionIdSpanAppender.java b/core/src/main/java/io/opentelemetry/android/SessionIdSpanAppender.java index 58453aa59..2888e55ab 100644 --- a/core/src/main/java/io/opentelemetry/android/SessionIdSpanAppender.java +++ b/core/src/main/java/io/opentelemetry/android/SessionIdSpanAppender.java @@ -23,7 +23,10 @@ public SessionIdSpanAppender(SessionProvider sessionProvider) { @Override public void onStart(Context parentContext, ReadWriteSpan span) { - span.setAttribute(SESSION_ID, sessionProvider.getSessionId()); + String sessionId = sessionProvider.getSessionId(); + if (sessionId != null) { + span.setAttribute(SESSION_ID, sessionId); + } } @Override diff --git a/core/src/main/java/io/opentelemetry/android/SessionIdTimeoutHandler.java b/core/src/main/java/io/opentelemetry/android/SessionIdTimeoutHandler.java deleted file mode 100644 index 215f0fa1e..000000000 --- a/core/src/main/java/io/opentelemetry/android/SessionIdTimeoutHandler.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.android; - -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. - */ -// TODO: Migrate to kotlin and make internal? -public final class SessionIdTimeoutHandler implements ApplicationStateListener { - - static final Duration DEFAULT_SESSION_TIMEOUT = Duration.ofMinutes(15); - private final Duration sessionTimeout; - - private final Clock clock; - private volatile long timeoutStartNanos; - private volatile State state = State.FOREGROUND; - - SessionIdTimeoutHandler() { - this(DEFAULT_SESSION_TIMEOUT); - } - - // for testing - SessionIdTimeoutHandler(Duration sessionTimeout) { - this(Clock.getDefault(), sessionTimeout); - } - - SessionIdTimeoutHandler(Clock clock, Duration sessionTimeout) { - this.clock = clock; - this.sessionTimeout = sessionTimeout; - } - - @Override - public void onApplicationForegrounded() { - state = State.TRANSITIONING_TO_FOREGROUND; - } - - @Override - public void onApplicationBackgrounded() { - state = State.BACKGROUND; - } - - public boolean hasTimedOut() { - // don't apply sessionId timeout to apps in the foreground - if (state == State.FOREGROUND) { - return false; - } - long elapsedTime = clock.nanoTime() - timeoutStartNanos; - return elapsedTime >= sessionTimeout.toNanos(); - } - - public void 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 State { - FOREGROUND, - BACKGROUND, - /** A temporary state representing the first event after the app has been brought back. */ - TRANSITIONING_TO_FOREGROUND - } -} diff --git a/core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java b/core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java index 5b277383b..5aa2f935f 100644 --- a/core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java +++ b/core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java @@ -6,11 +6,7 @@ package io.opentelemetry.android.config; import io.opentelemetry.android.ScreenAttributesSpanProcessor; -import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfiguration; import io.opentelemetry.android.internal.services.network.CurrentNetworkProvider; -import io.opentelemetry.api.common.Attributes; -import java.time.Duration; -import java.util.function.Supplier; /** * Configuration object for OpenTelemetry Android. The configuration items in this class will be @@ -19,36 +15,10 @@ */ public class OtelRumConfig { - private Supplier globalAttributesSupplier = Attributes::empty; private boolean includeNetworkAttributes = true; private boolean generateSdkInitializationEvents = true; private boolean includeScreenAttributes = true; private boolean discoverInstrumentations = true; - private DiskBufferingConfiguration diskBufferingConfiguration = - DiskBufferingConfiguration.builder().build(); - private Duration sessionTimeout = Duration.ofMinutes(15); - - /** - * Configures the set of global attributes to emit with every span and event. Any existing - * configured attributes will be dropped. Default = none. - */ - public OtelRumConfig setGlobalAttributes(Attributes attributes) { - return setGlobalAttributes(() -> attributes); - } - - public OtelRumConfig setGlobalAttributes(Supplier globalAttributesSupplier) { - this.globalAttributesSupplier = globalAttributesSupplier; - return this; - } - - public boolean hasGlobalAttributes() { - Attributes attributes = globalAttributesSupplier.get(); - return attributes != null && !attributes.isEmpty(); - } - - public Supplier getGlobalAttributesSupplier() { - return globalAttributesSupplier; - } /** * Disables the collection of runtime network attributes. See {@link CurrentNetworkProvider} for @@ -98,10 +68,6 @@ public boolean shouldIncludeScreenAttributes() { return includeScreenAttributes; } - public DiskBufferingConfiguration getDiskBufferingConfiguration() { - return diskBufferingConfiguration; - } - /** * Return {@link Boolean#TRUE} if the RUM initialization should look for instrumentations in the * classpath and apply them automatically. @@ -119,26 +85,4 @@ public OtelRumConfig disableInstrumentationDiscovery() { discoverInstrumentations = false; return this; } - - /** - * Sets the parameters for caching signals in disk in order to export them later. - * - * @return this - */ - public OtelRumConfig setDiskBufferingConfiguration( - DiskBufferingConfiguration diskBufferingConfiguration) { - this.diskBufferingConfiguration = diskBufferingConfiguration; - return this; - } - - /** Call this method to set session timeout in minutes */ - public OtelRumConfig setSessionTimeout(Duration sessionTimeout) { - this.sessionTimeout = sessionTimeout; - return this; - } - - /** Call this method to retrieve session timeout */ - public Duration getSessionTimeout() { - return sessionTimeout; - } } diff --git a/core/src/main/java/io/opentelemetry/android/internal/initialization/InitializationEvents.kt b/core/src/main/java/io/opentelemetry/android/internal/initialization/InitializationEvents.kt index 2d94abfdd..9f8a268a8 100644 --- a/core/src/main/java/io/opentelemetry/android/internal/initialization/InitializationEvents.kt +++ b/core/src/main/java/io/opentelemetry/android/internal/initialization/InitializationEvents.kt @@ -27,7 +27,7 @@ interface InitializationEvents { fun crashReportingInitialized() - fun spanExporterInitialized(spanExporter: SpanExporter) + fun spanExporterInitialized(spanExporter: SpanExporter?) companion object { private var instance: InitializationEvents? = null @@ -74,7 +74,7 @@ interface InitializationEvents { override fun crashReportingInitialized() {} - override fun spanExporterInitialized(spanExporter: SpanExporter) {} + override fun spanExporterInitialized(spanExporter: SpanExporter?) {} } } } diff --git a/core/src/main/java/io/opentelemetry/android/session/SessionProvider.kt b/core/src/main/java/io/opentelemetry/android/session/SessionProvider.kt index 6a0659cde..a5b143c03 100644 --- a/core/src/main/java/io/opentelemetry/android/session/SessionProvider.kt +++ b/core/src/main/java/io/opentelemetry/android/session/SessionProvider.kt @@ -6,5 +6,15 @@ package io.opentelemetry.android.session interface SessionProvider { - fun getSessionId(): String + companion object { + @JvmField + val NO_OP: SessionProvider = + object : SessionProvider { + override fun getSessionId(): String? { + return null + } + } + } + + fun getSessionId(): String? } diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/OtelDemoApplication.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/OtelDemoApplication.kt index fa0726f60..6a66200a3 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/OtelDemoApplication.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/OtelDemoApplication.kt @@ -9,19 +9,13 @@ import android.annotation.SuppressLint import android.app.Application import android.util.Log import io.opentelemetry.android.OpenTelemetryRum -import io.opentelemetry.android.OpenTelemetryRumBuilder -import io.opentelemetry.android.agent.setSlowRenderingDetectionPollInterval -import io.opentelemetry.android.config.OtelRumConfig -import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfiguration +import io.opentelemetry.android.agent.AndroidAgent +import io.opentelemetry.android.agent.endpoint.EndpointConfig import io.opentelemetry.api.common.AttributeKey.stringKey import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.incubator.events.EventBuilder import io.opentelemetry.api.trace.Tracer -import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter -import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider -import java.time.Duration -import kotlin.math.log const val TAG = "otel.demo" @@ -31,31 +25,12 @@ class OtelDemoApplication : Application() { super.onCreate() Log.i(TAG, "Initializing the opentelemetry-android-agent") - val diskBufferingConfig = - DiskBufferingConfiguration.builder() - .setEnabled(true) - .setMaxCacheSize(10_000_000) - .build() - val config = - OtelRumConfig() - .setGlobalAttributes(Attributes.of(stringKey("toolkit"), "jetpack compose")) - .setDiskBufferingConfiguration(diskBufferingConfig) - // 10.0.2.2 is apparently a special binding to the host running the emulator - val spansIngestUrl = "http://10.0.2.2:4318/v1/traces" - val logsIngestUrl = "http://10.0.2.2:4318/v1/logs" - val otelRumBuilder: OpenTelemetryRumBuilder = - OpenTelemetryRum.builder(this, config) - .addSpanExporterCustomizer { - OtlpHttpSpanExporter.builder() - .setEndpoint(spansIngestUrl) - .build() - } - .addLogRecordExporterCustomizer { - OtlpHttpLogRecordExporter.builder() - .setEndpoint(logsIngestUrl) - .build() - } + val otelRumBuilder = AndroidAgent.createRumBuilder( + this, + endpointConfig = EndpointConfig.getDefault("http://10.0.2.2:4318"), + ).setGlobalAttributes(Attributes.of(stringKey("toolkit"), "jetpack compose")) + try { rum = otelRumBuilder.build() Log.d(TAG, "RUM session started: " + rum!!.rumSessionId) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d5eb3cb9..a2a4c7c13 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ byteBuddy = "1.15.1" okhttp = "4.12.0" spotless = "6.25.0" kotlin = "2.0.20" -androidPlugin = "8.6.1" +androidPlugin = "8.5.2" junitKtx = "1.2.1" autoService = "1.1.1" diff --git a/instrumentation/startup/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.kt b/instrumentation/startup/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.kt index 728b8bb9e..dbf2cd8f5 100644 --- a/instrumentation/startup/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.kt +++ b/instrumentation/startup/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.kt @@ -51,7 +51,7 @@ class SdkInitializationEvents(private val clock: Supplier = Supplier { addEvent(RumConstants.Events.INIT_EVENT_CRASH_REPORTER) } - override fun spanExporterInitialized(spanExporter: SpanExporter) { + override fun spanExporterInitialized(spanExporter: SpanExporter?) { val attributes = Attributes.of(AttributeKey.stringKey("span.exporter"), spanExporter.toString()) addEvent(RumConstants.Events.INIT_EVENT_SPAN_EXPORTER, attr = attributes)