Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow building RUM with preconfigured SDK. #587

Merged
merged 4 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -28,14 +32,35 @@
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.
*/
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.
*
* <p>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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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<InstrumentedApplication> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Consumer<InstrumentedApplication>> 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<InstrumentedApplication> instrumentationInstaller) {
instrumentationInstallers.add(instrumentationInstaller);
return this;
}

/**
* Creates a new instance of {@link OpenTelemetryRum} with the settings of this {@link
* OpenTelemetryRumBuilder}.
*
* <p>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<InstrumentedApplication> installer : instrumentationInstallers) {
installer.accept(instrumentedApplication);
}

return new OpenTelemetryRumImpl(sdk, sessionId);
}
}