From a22049136e2956e68199d7f94a6781c01c60975b Mon Sep 17 00:00:00 2001 From: jason plumb <75337021+breedx-splk@users.noreply.github.com> Date: Thu, 6 Jul 2023 16:30:41 -0700 Subject: [PATCH] Allow building RUM with preconfigured SDK. (#587) * allow building RUM with preconfigured SDK. * Update opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRum.java Co-authored-by: Mateusz Rzeszutek * code review comments * add imports --------- Co-authored-by: Mateusz Rzeszutek --- .../rum/internal/OpenTelemetryRum.java | 27 +++++- .../rum/internal/OpenTelemetryRumBuilder.java | 27 ++---- .../internal/SdkPreconfiguredRumBuilder.java | 85 +++++++++++++++++++ 3 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/SdkPreconfiguredRumBuilder.java diff --git a/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRum.java b/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRum.java index c8d1a3dc..0851e7cf 100644 --- a/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRum.java +++ b/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRum.java @@ -18,6 +18,10 @@ import android.app.Application; 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; /** * Entrypoint for the OpenTelemetry Real User Monitoring library for Android. @@ -28,7 +32,10 @@ public interface OpenTelemetryRum { /** - * Returns a new {@link OpenTelemetryRumBuilder} for {@link OpenTelemetryRum}. + * Returns a new {@link OpenTelemetryRumBuilder} for {@link OpenTelemetryRum}. Use this version + * if you would like to configure individual aspects of the OpenTelemetry SDK but would still + * prefer to allow OpenTelemetry RUM to create the SDK for you. If you would like to "bring your + * own" SDK, call the two-argument version. * * @param application The {@link Application} that is being instrumented. */ @@ -36,6 +43,24 @@ static OpenTelemetryRumBuilder builder(Application application) { return new OpenTelemetryRumBuilder(application); } + /** + * Returns a new {@link SdkPreconfiguredRumBuilder} for {@link OpenTelemetryRum}. This version + * requires the user to preconfigure and create their own OpenTelemetrySdk instance. If you + * prefer to use the builder to configure individual aspects of the OpenTelemetry SDK and to + * create and manage it for you, call the one-argument version. + * + *

Specific consideration should be given to the creation of your provided SDK to ensure that + * the {@link SdkTracerProvider}, {@link SdkMeterProvider}, and {@link SdkLoggerProvider} are + * configured correctly for your target RUM provider. + * + * @param application The {@link Application} that is being instrumented. + * @param openTelemetrySdk The {@link OpenTelemetrySdk} that the user has already created. + */ + static SdkPreconfiguredRumBuilder builder( + Application application, OpenTelemetrySdk openTelemetrySdk) { + return new SdkPreconfiguredRumBuilder(application, openTelemetrySdk); + } + /** Returns a no-op implementation of {@link OpenTelemetryRum}. */ static OpenTelemetryRum noop() { return NoopOpenTelemetryRum.INSTANCE; diff --git a/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRumBuilder.java b/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRumBuilder.java index 6c419039..12d8b705 100644 --- a/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRumBuilder.java +++ b/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/OpenTelemetryRumBuilder.java @@ -17,7 +17,6 @@ package io.opentelemetry.rum.internal; import android.app.Application; -import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.rum.internal.instrumentation.InstrumentedApplication; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.logs.SdkLoggerProvider; @@ -54,8 +53,8 @@ public final class OpenTelemetryRumBuilder { private Resource resource; OpenTelemetryRumBuilder(Application application) { - SessionIdTimeoutHandler timeoutHandler = new SessionIdTimeoutHandler(); this.application = application; + SessionIdTimeoutHandler timeoutHandler = new SessionIdTimeoutHandler(); this.sessionId = new SessionId(timeoutHandler); this.resource = AndroidResource.createDefault(application); } @@ -168,31 +167,17 @@ public SessionId getSessionId() { * @return A new {@link OpenTelemetryRum} instance. */ public OpenTelemetryRum build() { - // 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 - ApplicationStateWatcher applicationStateWatcher = new ApplicationStateWatcher(); - application.registerActivityLifecycleCallbacks(applicationStateWatcher); - - applicationStateWatcher.registerListener(sessionId.getTimeoutHandler()); - - OpenTelemetrySdk openTelemetrySdk = + OpenTelemetrySdk sdk = OpenTelemetrySdk.builder() .setTracerProvider(buildTracerProvider(sessionId, application)) .setMeterProvider(buildMeterProvider(application)) .setLoggerProvider(buildLoggerProvider(application)) .build(); - Tracer tracer = openTelemetrySdk.getTracer(OpenTelemetryRum.class.getSimpleName()); - sessionId.setSessionIdChangeListener(new SessionIdChangeTracer(tracer)); - - InstrumentedApplication instrumentedApplication = - new InstrumentedApplicationImpl( - application, openTelemetrySdk, applicationStateWatcher); - for (Consumer installer : instrumentationInstallers) { - installer.accept(instrumentedApplication); - } - - return new OpenTelemetryRumImpl(openTelemetrySdk, sessionId); + SdkPreconfiguredRumBuilder delegate = + new SdkPreconfiguredRumBuilder(application, sdk, sessionId); + instrumentationInstallers.forEach(delegate::addInstrumentation); + return delegate.build(); } private SdkTracerProvider buildTracerProvider(SessionId sessionId, Application application) { diff --git a/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/SdkPreconfiguredRumBuilder.java b/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/SdkPreconfiguredRumBuilder.java new file mode 100644 index 00000000..667bb1a5 --- /dev/null +++ b/opentelemetry-android-instrumentation/src/main/java/io/opentelemetry/rum/internal/SdkPreconfiguredRumBuilder.java @@ -0,0 +1,85 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.rum.internal; + +import android.app.Application; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.rum.internal.instrumentation.InstrumentedApplication; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public final class SdkPreconfiguredRumBuilder { + private final Application application; + private final OpenTelemetrySdk sdk; + private final SessionId sessionId; + + private final List> instrumentationInstallers = + new ArrayList<>(); + + SdkPreconfiguredRumBuilder(Application application, OpenTelemetrySdk openTelemetrySdk) { + this(application, openTelemetrySdk, new SessionId(new SessionIdTimeoutHandler())); + } + + SdkPreconfiguredRumBuilder( + Application application, OpenTelemetrySdk openTelemetrySdk, SessionId sessionId) { + this.application = application; + this.sdk = openTelemetrySdk; + this.sessionId = sessionId; + } + + /** + * Adds an instrumentation installer function that will be run on an {@link + * InstrumentedApplication} instance as a part of the {@link #build()} method call. + * + * @return {@code this} + */ + public SdkPreconfiguredRumBuilder addInstrumentation( + Consumer instrumentationInstaller) { + instrumentationInstallers.add(instrumentationInstaller); + return this; + } + + /** + * Creates a new instance of {@link OpenTelemetryRum} with the settings of this {@link + * OpenTelemetryRumBuilder}. + * + *

This method uses a preconfigured OpenTelemetry SDK and install built-in system + * instrumentations in the passed Android {@link Application}. + * + * @return A new {@link OpenTelemetryRum} instance. + */ + public OpenTelemetryRum build() { + // 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 + ApplicationStateWatcher applicationStateWatcher = new ApplicationStateWatcher(); + application.registerActivityLifecycleCallbacks(applicationStateWatcher); + applicationStateWatcher.registerListener(sessionId.getTimeoutHandler()); + + Tracer tracer = sdk.getTracer(OpenTelemetryRum.class.getSimpleName()); + sessionId.setSessionIdChangeListener(new SessionIdChangeTracer(tracer)); + + InstrumentedApplication instrumentedApplication = + new InstrumentedApplicationImpl(application, sdk, applicationStateWatcher); + for (Consumer installer : instrumentationInstallers) { + installer.accept(instrumentedApplication); + } + + return new OpenTelemetryRumImpl(sdk, sessionId); + } +}