From 286171f8f7519c4a45c43c0c29e60ca02ccd5a72 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:54:22 +0200 Subject: [PATCH 01/16] Moving global attrs and disk buffering config over to OpenTelemetryRumBuilder --- .../android/OpenTelemetryRumBuilder.java | 54 +++++++++++++++---- .../android/config/OtelRumConfig.java | 43 --------------- 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index 1b33bd022..878308656 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -26,6 +26,7 @@ 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; @@ -56,6 +57,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; /** @@ -83,6 +85,9 @@ public final class OpenTelemetryRumBuilder { logRecordExporterCustomizer = a -> a; private Function propagatorCustomizer = (a) -> a; + private Supplier globalAttributesSupplier = Attributes::empty; + private DiskBufferingConfiguration diskBufferingConfiguration = + DiskBufferingConfiguration.builder().build(); private Resource resource; @@ -127,6 +132,36 @@ 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; + } + /** * 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 @@ -273,8 +308,6 @@ OpenTelemetryRum build(ServiceManager serviceManager) { InitializationEvents initializationEvents = InitializationEvents.get(); applyConfiguration(serviceManager, initializationEvents); - DiskBufferingConfiguration diskBufferingConfiguration = - config.getDiskBufferingConfiguration(); SpanExporter spanExporter = buildSpanExporter(); LogRecordExporter logsExporter = buildLogsExporter(); SignalFromDiskExporter signalFromDiskExporter = null; @@ -333,15 +366,15 @@ private StorageConfiguration createStorageConfiguration(ServiceManager serviceMa 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 +419,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)); @@ -443,8 +476,7 @@ private SdkLoggerProvider buildLoggerProvider( SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder() .addLogRecordProcessor( - new GlobalAttributesLogRecordAppender( - config.getGlobalAttributesSupplier())) + new GlobalAttributesLogRecordAppender(globalAttributesSupplier)) .setResource(resource); LogRecordProcessor batchLogsProcessor = BatchLogRecordProcessor.builder(logsExporter).build(); 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..00f5daafd 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,8 @@ 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,37 +16,12 @@ */ 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 * more information. Default = true. @@ -98,10 +70,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. @@ -120,17 +88,6 @@ public OtelRumConfig disableInstrumentationDiscovery() { 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; From 1c9784b0dd48708aefeb76408694915f03d95d28 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:37:50 +0200 Subject: [PATCH 02/16] Adding exporter setters --- .../android/OpenTelemetryRumBuilder.java | 99 ++++++++----------- 1 file changed, 40 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index 878308656..5ae04633a 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -32,11 +32,11 @@ 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; @@ -45,6 +45,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; @@ -80,9 +82,9 @@ public final class OpenTelemetryRumBuilder { 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 SpanExporter spanExporter; + private LogRecordExporter logRecordExporter; + private MetricExporter metricExporter; private Function propagatorCustomizer = (a) -> a; private Supplier globalAttributesSupplier = Attributes::empty; @@ -162,6 +164,18 @@ public OpenTelemetryRumBuilder setDiskBufferingConfiguration( 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 @@ -252,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}. @@ -308,8 +284,9 @@ OpenTelemetryRum build(ServiceManager serviceManager) { InitializationEvents initializationEvents = InitializationEvents.get(); applyConfiguration(serviceManager, initializationEvents); - 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 { @@ -321,11 +298,22 @@ OpenTelemetryRum build(ServiceManager serviceManager) { final LogRecordExporter originalLogsExporter = logsExporter; logsExporter = LogRecordToDiskExporter.create(originalLogsExporter, storageConfiguration); + final MetricExporter originalMetricExporter = metricExporter; + if (originalMetricExporter != null) { + metricExporter = + MetricToDiskExporter.create( + metricExporter, + storageConfiguration, + metricExporter::getAggregationTemporality); + } signalFromDiskExporter = new SignalFromDiskExporter( SpanFromDiskExporter.create( originalSpanExporter, storageConfiguration), - null, + (originalMetricExporter != null) + ? MetricFromDiskExporter.create( + originalMetricExporter, storageConfiguration) + : null, LogRecordFromDiskExporter.create( originalLogsExporter, storageConfiguration)); } catch (IOException e) { @@ -341,7 +329,7 @@ OpenTelemetryRum build(ServiceManager serviceManager) { OpenTelemetrySdk.builder() .setTracerProvider( buildTracerProvider(sessionManager, application, spanExporter)) - .setMeterProvider(buildMeterProvider(application)) + .setMeterProvider(buildMeterProvider(application, metricExporter)) .setLoggerProvider(buildLoggerProvider(application, logsExporter)) .setPropagators(buildFinalPropagators()) .build(); @@ -488,20 +476,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); From 21afb48e38f8ca084c0a9236a08d44ab7d56cbf9 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:47:44 +0200 Subject: [PATCH 03/16] Validating nullable exporters --- .../android/OpenTelemetryRumBuilder.java | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index 5ae04633a..f5c379da9 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -293,11 +293,16 @@ OpenTelemetryRum build(ServiceManager serviceManager) { 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 = @@ -307,15 +312,11 @@ OpenTelemetryRum build(ServiceManager serviceManager) { metricExporter::getAggregationTemporality); } signalFromDiskExporter = - new SignalFromDiskExporter( - SpanFromDiskExporter.create( - originalSpanExporter, storageConfiguration), - (originalMetricExporter != null) - ? MetricFromDiskExporter.create( - originalMetricExporter, 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); } @@ -350,6 +351,24 @@ OpenTelemetryRum build(ServiceManager serviceManager) { 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(); @@ -443,14 +462,19 @@ private void applyConfiguration( } private SdkTracerProvider buildTracerProvider( - SessionProvider sessionProvider, Application application, SpanExporter spanExporter) { + SessionProvider sessionProvider, + 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) { @@ -460,15 +484,17 @@ private SdkTracerProvider buildTracerProvider( } private SdkLoggerProvider buildLoggerProvider( - Application application, LogRecordExporter logsExporter) { + Application application, @Nullable LogRecordExporter logsExporter) { SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder() .addLogRecordProcessor( 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); From 95a158ac72eab82226e666c60fb8ddc29de996e6 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:39:33 +0200 Subject: [PATCH 04/16] Using SessionProvider instead of SessionManager --- .../android/OpenTelemetryRum.java | 10 +++++- .../android/OpenTelemetryRumBuilder.java | 35 ++++++++----------- .../android/OpenTelemetryRumImpl.java | 12 ++++--- .../android/SdkPreconfiguredRumBuilder.kt | 11 ++---- .../android/SessionIdSpanAppender.java | 5 ++- .../initialization/InitializationEvents.kt | 4 +-- .../android/session/SessionProvider.kt | 12 ++++++- .../startup/SdkInitializationEvents.kt | 2 +- 8 files changed, 51 insertions(+), 40 deletions(-) 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 f5c379da9..2cc25502b 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -23,7 +23,6 @@ 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; @@ -81,10 +80,10 @@ 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 SpanExporter spanExporter; - private LogRecordExporter logRecordExporter; - private MetricExporter metricExporter; + private SessionProvider sessionProvider = SessionProvider.getNoop(); + @Nullable private SpanExporter spanExporter; + @Nullable private LogRecordExporter logRecordExporter; + @Nullable private MetricExporter metricExporter; private Function propagatorCustomizer = (a) -> a; private Supplier globalAttributesSupplier = Attributes::empty; @@ -99,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; } @@ -122,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 @@ -309,7 +309,7 @@ OpenTelemetryRum build(ServiceManager serviceManager) { MetricToDiskExporter.create( metricExporter, storageConfiguration, - metricExporter::getAggregationTemporality); + originalMetricExporter::getAggregationTemporality); } signalFromDiskExporter = getSignalFromDiskExporter( @@ -323,13 +323,9 @@ OpenTelemetryRum build(ServiceManager serviceManager) { } initializationEvents.spanExporterInitialized(spanExporter); - SessionManager sessionManager = - SessionManager.create(timeoutHandler, config.getSessionTimeout().toNanos()); - OpenTelemetrySdk sdk = OpenTelemetrySdk.builder() - .setTracerProvider( - buildTracerProvider(sessionManager, application, spanExporter)) + .setTracerProvider(buildTracerProvider(application, spanExporter)) .setMeterProvider(buildMeterProvider(application, metricExporter)) .setLoggerProvider(buildLoggerProvider(application, logsExporter)) .setPropagators(buildFinalPropagators()) @@ -343,8 +339,7 @@ OpenTelemetryRum build(ServiceManager serviceManager) { new SdkPreconfiguredRumBuilder( application, sdk, - timeoutHandler, - sessionManager, + sessionProvider, config.shouldDiscoverInstrumentations(), serviceManager); instrumentations.forEach(delegate::addInstrumentation); @@ -462,9 +457,7 @@ private void applyConfiguration( } private SdkTracerProvider buildTracerProvider( - SessionProvider sessionProvider, - Application application, - @Nullable SpanExporter spanExporter) { + Application application, @Nullable SpanExporter spanExporter) { SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder() .setResource(resource) 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/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..6e72680b1 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 { + @JvmStatic + val noop: SessionProvider = + object : SessionProvider { + override fun getSessionId(): String? { + return null + } + } + } + + fun getSessionId(): String? } 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) From 278660bc6b9c7aa4809b42a36481e8d447992ca0 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:47:12 +0200 Subject: [PATCH 05/16] Moving session-related helpers over to the agent module --- android-agent/build.gradle.kts | 1 + .../kotlin/io/opentelemetry/android/agent}/session/Session.kt | 2 +- .../android/agent}/session/SessionIdGenerator.kt | 2 +- .../android/agent/session}/SessionIdTimeoutHandler.java | 2 +- .../io/opentelemetry/android/agent}/session/SessionManager.kt | 4 ++-- .../opentelemetry/android/agent}/session/SessionObserver.kt | 2 +- .../opentelemetry/android/agent}/session/SessionPublisher.kt | 2 +- .../io/opentelemetry/android/agent}/session/SessionStorage.kt | 2 +- gradle/libs.versions.toml | 2 +- 9 files changed, 10 insertions(+), 9 deletions(-) rename {core/src/main/java/io/opentelemetry/android => android-agent/src/main/kotlin/io/opentelemetry/android/agent}/session/Session.kt (94%) rename {core/src/main/java/io/opentelemetry/android => android-agent/src/main/kotlin/io/opentelemetry/android/agent}/session/SessionIdGenerator.kt (92%) rename {core/src/main/java/io/opentelemetry/android => android-agent/src/main/kotlin/io/opentelemetry/android/agent/session}/SessionIdTimeoutHandler.java (98%) rename {core/src/main/java/io/opentelemetry/android => android-agent/src/main/kotlin/io/opentelemetry/android/agent}/session/SessionManager.kt (96%) rename {core/src/main/java/io/opentelemetry/android => android-agent/src/main/kotlin/io/opentelemetry/android/agent}/session/SessionObserver.kt (84%) rename {core/src/main/java/io/opentelemetry/android => android-agent/src/main/kotlin/io/opentelemetry/android/agent}/session/SessionPublisher.kt (77%) rename {core/src/main/java/io/opentelemetry/android => android-agent/src/main/kotlin/io/opentelemetry/android/agent}/session/SessionStorage.kt (90%) diff --git a/android-agent/build.gradle.kts b/android-agent/build.gradle.kts index a162286fe..cb7a58194 100644 --- a/android-agent/build.gradle.kts +++ b/android-agent/build.gradle.kts @@ -9,6 +9,7 @@ android { dependencies { api(project(":core")) + implementation(libs.opentelemetry.sdk) implementation(libs.opentelemetry.instrumentation.api) // Default instrumentations: 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/core/src/main/java/io/opentelemetry/android/SessionIdTimeoutHandler.java b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java similarity index 98% rename from core/src/main/java/io/opentelemetry/android/SessionIdTimeoutHandler.java rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java index 215f0fa1e..a91e912b0 100644 --- a/core/src/main/java/io/opentelemetry/android/SessionIdTimeoutHandler.java +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.android; +package io.opentelemetry.android.agent.session; import io.opentelemetry.android.internal.services.applifecycle.ApplicationStateListener; import io.opentelemetry.sdk.common.Clock; 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 96% 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..68c407d85 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,9 +3,9 @@ * 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 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/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" From ca958bc57145f5113156625d2ab7fffb60347b50 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:51:08 +0200 Subject: [PATCH 06/16] Making SessionProvider no_op a jvmfield --- .../io/opentelemetry/android/OpenTelemetryRumBuilder.java | 2 +- .../java/io/opentelemetry/android/session/SessionProvider.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index 2cc25502b..6c64829f7 100644 --- a/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -80,7 +80,7 @@ public final class OpenTelemetryRumBuilder { private final OtelRumConfig config; private final List instrumentations = new ArrayList<>(); private final List> otelSdkReadyListeners = new ArrayList<>(); - private SessionProvider sessionProvider = SessionProvider.getNoop(); + private SessionProvider sessionProvider = SessionProvider.NO_OP; @Nullable private SpanExporter spanExporter; @Nullable private LogRecordExporter logRecordExporter; @Nullable private MetricExporter metricExporter; 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 6e72680b1..a5b143c03 100644 --- a/core/src/main/java/io/opentelemetry/android/session/SessionProvider.kt +++ b/core/src/main/java/io/opentelemetry/android/session/SessionProvider.kt @@ -7,8 +7,8 @@ package io.opentelemetry.android.session interface SessionProvider { companion object { - @JvmStatic - val noop: SessionProvider = + @JvmField + val NO_OP: SessionProvider = object : SessionProvider { override fun getSessionId(): String? { return null From ee7887edbffc094f22ac8cb39bf8931676694fd9 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:12:28 +0200 Subject: [PATCH 07/16] Created AndroidAgent --- .../android/agent/AndroidAgent.kt | 119 ++++++++++++++++++ .../android/agent/OtelRumConfigExtensions.kt | 80 ------------ 2 files changed, 119 insertions(+), 80 deletions(-) create mode 100644 android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt delete mode 100644 android-agent/src/main/kotlin/io/opentelemetry/android/agent/OtelRumConfigExtensions.kt 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..6a9970ef4 --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt @@ -0,0 +1,119 @@ +/* + * 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.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 + * ``` + */ +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(), + 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) + + applyInstrumentationConfigs( + activityTracerCustomizer, + activityNameExtractor, + fragmentTracerCustomizer, + fragmentNameExtractor, + anrAttributesExtractor, + crashAttributesExtractor, + networkChangeAttributesExtractor, + slowRenderingDetectionPollInterval, + ) + + return rumBuilder + } + + 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 -} From 4ad7aa79b50ed2ccceccc36a6f41cf373b0c60fc Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:08:55 +0200 Subject: [PATCH 08/16] Configuring session provider from the agent --- .../android/agent/AndroidAgent.kt | 19 +++++++++++++++++++ .../session/SessionIdTimeoutHandler.java | 2 +- .../android/agent/session/SessionManager.kt | 10 ++++++---- .../android/config/OtelRumConfig.java | 13 ------------- 4 files changed, 26 insertions(+), 18 deletions(-) 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 index 6a9970ef4..42a2eaeee 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt @@ -8,6 +8,8 @@ package io.opentelemetry.android.agent import android.app.Application import io.opentelemetry.android.OpenTelemetryRum import io.opentelemetry.android.OpenTelemetryRumBuilder +import io.opentelemetry.android.agent.session.SessionIdTimeoutHandler +import io.opentelemetry.android.agent.session.SessionManager import io.opentelemetry.android.config.OtelRumConfig import io.opentelemetry.android.instrumentation.AndroidInstrumentationLoader import io.opentelemetry.android.instrumentation.activity.ActivityLifecycleInstrumentation @@ -18,9 +20,11 @@ import io.opentelemetry.android.instrumentation.crash.CrashReporterInstrumentati 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.instrumentation.api.instrumenter.AttributesExtractor +import io.opentelemetry.sdk.common.Clock import java.time.Duration /** @@ -64,6 +68,7 @@ object AndroidAgent { fun createRumBuilder( application: Application, otelRumConfig: OtelRumConfig = OtelRumConfig(), + sessionTimeout: Duration = Duration.ofMinutes(15), activityTracerCustomizer: ((Tracer) -> Tracer)? = null, activityNameExtractor: ScreenNameExtractor? = null, fragmentTracerCustomizer: ((Tracer) -> Tracer)? = null, @@ -75,6 +80,8 @@ object AndroidAgent { ): OpenTelemetryRumBuilder { val rumBuilder = OpenTelemetryRum.builder(application, otelRumConfig) + configureSessionProvider(rumBuilder, sessionTimeout) + applyInstrumentationConfigs( activityTracerCustomizer, activityNameExtractor, @@ -89,6 +96,18 @@ object AndroidAgent { 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 applyInstrumentationConfigs( activityTracerCustomizer: ((Tracer) -> Tracer)?, activityNameExtractor: ScreenNameExtractor?, diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java index a91e912b0..d131c8137 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java @@ -42,7 +42,7 @@ public final class SessionIdTimeoutHandler implements ApplicationStateListener { this(Clock.getDefault(), sessionTimeout); } - SessionIdTimeoutHandler(Clock clock, Duration sessionTimeout) { + public SessionIdTimeoutHandler(Clock clock, Duration sessionTimeout) { this.clock = clock; this.sessionTimeout = sessionTimeout; } diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionManager.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionManager.kt index 68c407d85..3f6539b87 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionManager.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionManager.kt @@ -11,11 +11,11 @@ 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/config/OtelRumConfig.java b/core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java index 00f5daafd..5aa2f935f 100644 --- a/core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java +++ b/core/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java @@ -7,7 +7,6 @@ import io.opentelemetry.android.ScreenAttributesSpanProcessor; import io.opentelemetry.android.internal.services.network.CurrentNetworkProvider; -import java.time.Duration; /** * Configuration object for OpenTelemetry Android. The configuration items in this class will be @@ -20,7 +19,6 @@ public class OtelRumConfig { private boolean generateSdkInitializationEvents = true; private boolean includeScreenAttributes = true; private boolean discoverInstrumentations = true; - private Duration sessionTimeout = Duration.ofMinutes(15); /** * Disables the collection of runtime network attributes. See {@link CurrentNetworkProvider} for @@ -87,15 +85,4 @@ public OtelRumConfig disableInstrumentationDiscovery() { discoverInstrumentations = false; 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; - } } From b21c8fb32dfc4ea1de41f0ae7d307a5f42281794 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:51:25 +0200 Subject: [PATCH 09/16] Created EndpointConfig --- .../agent/endpoint/DefaultEndpointConfig.kt | 19 +++++++++++++++++ .../android/agent/endpoint/EndpointConfig.kt | 21 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultEndpointConfig.kt create mode 100644 android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/EndpointConfig.kt diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultEndpointConfig.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultEndpointConfig.kt new file mode 100644 index 000000000..924ff1882 --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultEndpointConfig.kt @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.agent.endpoint + +internal data class DefaultEndpointConfig( + private val url: String, + private val headers: Map, +) : EndpointConfig { + override fun getUrl(): String { + return url + } + + 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..9b3980e0f --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/EndpointConfig.kt @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.android.agent.endpoint + +interface EndpointConfig { + fun getUrl(): String + + fun getHeaders(): Map + + companion object { + fun getDefault( + url: String, + headers: Map = emptyMap(), + ): EndpointConfig { + return DefaultEndpointConfig(url, headers) + } + } +} From ee26ccc169dd0e9c93167c6e17e2cb96c65f2c49 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:03:46 +0200 Subject: [PATCH 10/16] Making EndpointConfig.Companion.getDefault jvmstatic --- .../io/opentelemetry/android/agent/endpoint/EndpointConfig.kt | 1 + 1 file changed, 1 insertion(+) 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 index 9b3980e0f..4fe8b262e 100644 --- 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 @@ -11,6 +11,7 @@ interface EndpointConfig { fun getHeaders(): Map companion object { + @JvmStatic fun getDefault( url: String, headers: Map = emptyMap(), From 753d121a0d5c8e507bda9bbbfc7315410f166586 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:23:28 +0200 Subject: [PATCH 11/16] Configuring span and log exporters from the agent --- android-agent/build.gradle.kts | 1 + .../android/agent/AndroidAgent.kt | 29 +++++++++++++++++++ ...Config.kt => DefaultHttpEndpointConfig.kt} | 12 +++++--- .../android/agent/endpoint/EndpointConfig.kt | 8 +++-- 4 files changed, 43 insertions(+), 7 deletions(-) rename android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/{DefaultEndpointConfig.kt => DefaultHttpEndpointConfig.kt} (53%) diff --git a/android-agent/build.gradle.kts b/android-agent/build.gradle.kts index cb7a58194..49073d4e6 100644 --- a/android-agent/build.gradle.kts +++ b/android-agent/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { api(project(":core")) implementation(libs.opentelemetry.sdk) implementation(libs.opentelemetry.instrumentation.api) + 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 index 42a2eaeee..26239d247 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt @@ -8,6 +8,7 @@ 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 @@ -23,6 +24,8 @@ import io.opentelemetry.android.instrumentation.slowrendering.SlowRenderingInstr 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 @@ -68,6 +71,7 @@ object AndroidAgent { fun createRumBuilder( application: Application, otelRumConfig: OtelRumConfig = OtelRumConfig(), + endpointConfig: EndpointConfig = EndpointConfig.getDefault("http://10.0.2.2:4318"), sessionTimeout: Duration = Duration.ofMinutes(15), activityTracerCustomizer: ((Tracer) -> Tracer)? = null, activityNameExtractor: ScreenNameExtractor? = null, @@ -81,6 +85,7 @@ object AndroidAgent { val rumBuilder = OpenTelemetryRum.builder(application, otelRumConfig) configureSessionProvider(rumBuilder, sessionTimeout) + configureExporters(rumBuilder, endpointConfig) applyInstrumentationConfigs( activityTracerCustomizer, @@ -108,6 +113,30 @@ object AndroidAgent { } } + 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 applyInstrumentationConfigs( activityTracerCustomizer: ((Tracer) -> Tracer)?, activityNameExtractor: ScreenNameExtractor?, diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultEndpointConfig.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultHttpEndpointConfig.kt similarity index 53% rename from android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultEndpointConfig.kt rename to android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultHttpEndpointConfig.kt index 924ff1882..9f03eea6e 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultEndpointConfig.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/endpoint/DefaultHttpEndpointConfig.kt @@ -5,12 +5,16 @@ package io.opentelemetry.android.agent.endpoint -internal data class DefaultEndpointConfig( - private val url: String, +internal data class DefaultHttpEndpointConfig( + private val baseUrl: String, private val headers: Map, ) : EndpointConfig { - override fun getUrl(): String { - return url + override fun getSpanExporterUrl(): String { + return "$baseUrl/v1/traces" + } + + override fun getLogRecordExporterUrl(): String { + return "$baseUrl/v1/logs" } override fun getHeaders(): Map { 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 index 4fe8b262e..8cc35170c 100644 --- 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 @@ -6,17 +6,19 @@ package io.opentelemetry.android.agent.endpoint interface EndpointConfig { - fun getUrl(): String + fun getSpanExporterUrl(): String + + fun getLogRecordExporterUrl(): String fun getHeaders(): Map companion object { @JvmStatic fun getDefault( - url: String, + baseUrl: String, headers: Map = emptyMap(), ): EndpointConfig { - return DefaultEndpointConfig(url, headers) + return DefaultHttpEndpointConfig(baseUrl.trimEnd('/'), headers) } } } From 88662d3b47cc50ae603e73ccb5570f1c1df3d586 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:59:27 +0200 Subject: [PATCH 12/16] Configuring disk buffering in the agent --- android-agent/build.gradle.kts | 1 - .../android/agent/AndroidAgent.kt | 12 +++++- core/build.gradle.kts | 2 +- .../android/demo/OtelDemoApplication.kt | 39 ++++--------------- 4 files changed, 19 insertions(+), 35 deletions(-) diff --git a/android-agent/build.gradle.kts b/android-agent/build.gradle.kts index 49073d4e6..9e9f9fdb2 100644 --- a/android-agent/build.gradle.kts +++ b/android-agent/build.gradle.kts @@ -10,7 +10,6 @@ android { dependencies { api(project(":core")) implementation(libs.opentelemetry.sdk) - implementation(libs.opentelemetry.instrumentation.api) implementation(libs.opentelemetry.exporter.otlp) // Default instrumentations: 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 index 26239d247..44c80cb7f 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt @@ -12,6 +12,7 @@ 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 @@ -71,7 +72,7 @@ object AndroidAgent { fun createRumBuilder( application: Application, otelRumConfig: OtelRumConfig = OtelRumConfig(), - endpointConfig: EndpointConfig = EndpointConfig.getDefault("http://10.0.2.2:4318"), + endpointConfig: EndpointConfig = EndpointConfig.getDefault("http://localhost"), sessionTimeout: Duration = Duration.ofMinutes(15), activityTracerCustomizer: ((Tracer) -> Tracer)? = null, activityNameExtractor: ScreenNameExtractor? = null, @@ -86,6 +87,7 @@ object AndroidAgent { configureSessionProvider(rumBuilder, sessionTimeout) configureExporters(rumBuilder, endpointConfig) + configureDiskBuffering(rumBuilder) applyInstrumentationConfigs( activityTracerCustomizer, @@ -137,6 +139,14 @@ object AndroidAgent { 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?, diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6d32acda9..60daf5cca 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -68,10 +68,10 @@ 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/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) From a9fa47d88ced7e300c10b8e98000c24c6cb1b322 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:09:12 +0200 Subject: [PATCH 13/16] Rename .java to .kt --- .../android/agent/AndroidAgent.kt | 2 +- .../session/SessionIdTimeoutHandler.java | 84 ------------------- .../agent/session/SessionIdTimeoutHandler.kt | 71 ++++++++++++++++ 3 files changed, 72 insertions(+), 85 deletions(-) delete mode 100644 android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java create mode 100644 android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.kt 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 index 44c80cb7f..c5b887ef6 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt @@ -73,7 +73,7 @@ object AndroidAgent { application: Application, otelRumConfig: OtelRumConfig = OtelRumConfig(), endpointConfig: EndpointConfig = EndpointConfig.getDefault("http://localhost"), - sessionTimeout: Duration = Duration.ofMinutes(15), + sessionTimeout: Duration = SessionIdTimeoutHandler.DEFAULT_SESSION_TIMEOUT, activityTracerCustomizer: ((Tracer) -> Tracer)? = null, activityNameExtractor: ScreenNameExtractor? = null, fragmentTracerCustomizer: ((Tracer) -> Tracer)? = null, diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java deleted file mode 100644 index d131c8137..000000000 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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. - */ -// 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); - } - - public 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/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..4021fa9ef --- /dev/null +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/session/SessionIdTimeoutHandler.kt @@ -0,0 +1,71 @@ +/* + * 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) + } +} From eced527cd38b8c5775c938f47d5acffca8144afa Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:11:33 +0200 Subject: [PATCH 14/16] cFormatting --- .../android/agent/session/SessionIdTimeoutHandler.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 index 4021fa9ef..90c71213f 100644 --- 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 @@ -23,8 +23,7 @@ import java.time.Duration internal class SessionIdTimeoutHandler( private val clock: Clock = Clock.getDefault(), private val sessionTimeout: Duration = DEFAULT_SESSION_TIMEOUT, -) : - ApplicationStateListener { +) : ApplicationStateListener { @Volatile private var timeoutStartNanos: Long = 0 From 8eff5f1eb229045176e995a18d8aa096ba767965 Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:14:40 +0200 Subject: [PATCH 15/16] Clean up --- core/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 60daf5cca..6ffe2f0b5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -71,7 +71,6 @@ dependencies { api(libs.opentelemetry.instrumentation.api) implementation(libs.opentelemetry.sdk) implementation(libs.opentelemetry.api.incubator) - implementation(libs.opentelemetry.exporter.logging) implementation(libs.opentelemetry.semconv.incubating) implementation(libs.opentelemetry.diskBuffering) testImplementation(libs.opentelemetry.api.incubator) From b19c4cf5c7837a7b6fbf7e0af60d5e113eaf2fff Mon Sep 17 00:00:00 2001 From: Cesar Munoz <56847527+LikeTheSalad@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:16:43 +0200 Subject: [PATCH 16/16] Clean up --- .../io/opentelemetry/android/agent/AndroidAgent.kt | 10 ---------- 1 file changed, 10 deletions(-) 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 index c5b887ef6..34929e2dc 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/AndroidAgent.kt @@ -31,16 +31,6 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor import io.opentelemetry.sdk.common.Clock 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 - * ``` - */ object AndroidAgent { private val activityLifecycleInstrumentation by lazy { AndroidInstrumentationLoader.getInstrumentation(