Skip to content

Commit

Permalink
Extract unified lifecycle instrumentation to new class (#483)
Browse files Browse the repository at this point in the history
* move lifecycle instrumentation install code to otel

* add javadoc

* move creation into lambda so we don't front-load

* spotless

* factor out builder.

* spotless
  • Loading branch information
breedx-splk authored Feb 24, 2023
1 parent 674adb0 commit a49c644
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 109 deletions.
128 changes: 19 additions & 109 deletions splunk-otel-android/src/main/java/com/splunk/rum/RumInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,10 @@
import io.opentelemetry.rum.internal.GlobalAttributesSpanAppender;
import io.opentelemetry.rum.internal.OpenTelemetryRum;
import io.opentelemetry.rum.internal.OpenTelemetryRumBuilder;
import io.opentelemetry.rum.internal.instrumentation.InstrumentedApplication;
import io.opentelemetry.rum.internal.instrumentation.activity.ActivityCallbacks;
import io.opentelemetry.rum.internal.instrumentation.activity.ActivityTracerCache;
import io.opentelemetry.rum.internal.instrumentation.activity.Pre29ActivityCallbacks;
import io.opentelemetry.rum.internal.instrumentation.activity.Pre29VisibleScreenLifecycleBinding;
import io.opentelemetry.rum.internal.instrumentation.activity.RumFragmentActivityRegisterer;
import io.opentelemetry.rum.internal.instrumentation.activity.VisibleScreenLifecycleBinding;
import io.opentelemetry.rum.internal.instrumentation.activity.VisibleScreenTracker;
import io.opentelemetry.rum.internal.instrumentation.anr.AnrDetector;
import io.opentelemetry.rum.internal.instrumentation.crash.CrashReporter;
import io.opentelemetry.rum.internal.instrumentation.fragment.RumFragmentLifecycleCallbacks;
import io.opentelemetry.rum.internal.instrumentation.lifecycle.AndroidLifecycleInstrumentation;
import io.opentelemetry.rum.internal.instrumentation.network.CurrentNetworkProvider;
import io.opentelemetry.rum.internal.instrumentation.network.NetworkAttributesSpanAppender;
import io.opentelemetry.rum.internal.instrumentation.network.NetworkChangeMonitor;
Expand Down Expand Up @@ -206,116 +199,33 @@ SplunkRum initialize(
private void installLifecycleInstrumentations(
OpenTelemetryRumBuilder otelRumBuilder, VisibleScreenTracker visibleScreenTracker) {

installStartupTimerInstrumentation(otelRumBuilder);
installActivityLifecycleEventsInstrumentation(otelRumBuilder, visibleScreenTracker);
installFragmentLifecycleInstrumentation(otelRumBuilder, visibleScreenTracker);
installScreenTrackingInstrumentation(otelRumBuilder, visibleScreenTracker);

otelRumBuilder.addInstrumentation(
instrumentedApp -> {
Function<Tracer, Tracer> tracerCustomizer =
tracer ->
(Tracer)
spanName -> {
String component =
spanName.equals(APP_START_SPAN_NAME)
? COMPONENT_APPSTART
: COMPONENT_UI;
return tracer.spanBuilder(spanName)
.setAttribute(COMPONENT_KEY, component);
};
AndroidLifecycleInstrumentation instrumentation =
AndroidLifecycleInstrumentation.builder()
.setVisibleScreenTracker(visibleScreenTracker)
.setStartupTimer(startupTimer)
.setTracerCustomizer(tracerCustomizer)
.build();
instrumentation.installOn(instrumentedApp);
initializationEvents.add(
new InitializationEvent(
"activityLifecycleCallbacksInitialized",
startupTimer.clockNow()));
});
}

private void installScreenTrackingInstrumentation(
OpenTelemetryRumBuilder otelRumBuilder, VisibleScreenTracker visibleScreenTracker) {
otelRumBuilder.addInstrumentation(
instrumentedApp -> {
Application.ActivityLifecycleCallbacks screenTrackingBinding =
buildScreenTrackingBinding(visibleScreenTracker);
instrumentedApp
.getApplication()
.registerActivityLifecycleCallbacks(screenTrackingBinding);
});
}

@NonNull
private Application.ActivityLifecycleCallbacks buildScreenTrackingBinding(
VisibleScreenTracker visibleScreenTracker) {
if (Build.VERSION.SDK_INT < 29) {
return new Pre29VisibleScreenLifecycleBinding(visibleScreenTracker);
}
return new VisibleScreenLifecycleBinding(visibleScreenTracker);
}

private void installFragmentLifecycleInstrumentation(
OpenTelemetryRumBuilder otelRumBuilder, VisibleScreenTracker visibleScreenTracker) {
otelRumBuilder.addInstrumentation(
instrumentedApp -> {
Application.ActivityLifecycleCallbacks fragmentRegisterer =
buildFragmentRegisterer(visibleScreenTracker, instrumentedApp);
instrumentedApp
.getApplication()
.registerActivityLifecycleCallbacks(fragmentRegisterer);
});
}

@NonNull
private Application.ActivityLifecycleCallbacks buildFragmentRegisterer(
VisibleScreenTracker visibleScreenTracker, InstrumentedApplication instrumentedApp) {
Tracer delegateTracer = instrumentedApp.getOpenTelemetrySdk().getTracer(RUM_TRACER_NAME);
Tracer tracer =
spanName ->
delegateTracer
.spanBuilder(spanName)
.setAttribute(COMPONENT_KEY, COMPONENT_UI);
RumFragmentLifecycleCallbacks fragmentLifecycle =
new RumFragmentLifecycleCallbacks(tracer, visibleScreenTracker);
if (Build.VERSION.SDK_INT < 29) {
return RumFragmentActivityRegisterer.createPre29(fragmentLifecycle);
}
return RumFragmentActivityRegisterer.create(fragmentLifecycle);
}

private void installStartupTimerInstrumentation(OpenTelemetryRumBuilder otelRumBuilder) {
otelRumBuilder.addInstrumentation(
instrumentedApp -> {
instrumentedApp
.getApplication()
.registerActivityLifecycleCallbacks(
startupTimer.createLifecycleCallback());
});
}

private void installActivityLifecycleEventsInstrumentation(
OpenTelemetryRumBuilder otelRumBuilder, VisibleScreenTracker visibleScreenTracker) {
otelRumBuilder.addInstrumentation(
instrumentedApp -> {
Application.ActivityLifecycleCallbacks activityCallbacks =
buildActivityEventsCallback(visibleScreenTracker, instrumentedApp);
instrumentedApp
.getApplication()
.registerActivityLifecycleCallbacks(activityCallbacks);
});
}

@NonNull
private Application.ActivityLifecycleCallbacks buildActivityEventsCallback(
VisibleScreenTracker visibleScreenTracker, InstrumentedApplication instrumentedApp) {
Tracer delegateTracer = instrumentedApp.getOpenTelemetrySdk().getTracer(RUM_TRACER_NAME);
Tracer tracer =
spanName -> {
// override the component to be appstart when appstart
String component =
spanName.equals(APP_START_SPAN_NAME)
? COMPONENT_APPSTART
: COMPONENT_UI;
return delegateTracer
.spanBuilder(spanName)
.setAttribute(COMPONENT_KEY, component);
};

ActivityTracerCache tracers =
new ActivityTracerCache(tracer, visibleScreenTracker, startupTimer);
if (Build.VERSION.SDK_INT < 29) {
return new Pre29ActivityCallbacks(tracers);
}
return new ActivityCallbacks(tracers);
}

private Resource createResource() {
// applicationName can't be null at this stage
String applicationName = requireNonNull(builder.applicationName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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.instrumentation.lifecycle;

import android.app.Application;
import android.os.Build;
import androidx.annotation.NonNull;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.rum.internal.instrumentation.InstrumentedApplication;
import io.opentelemetry.rum.internal.instrumentation.activity.ActivityCallbacks;
import io.opentelemetry.rum.internal.instrumentation.activity.ActivityTracerCache;
import io.opentelemetry.rum.internal.instrumentation.activity.Pre29ActivityCallbacks;
import io.opentelemetry.rum.internal.instrumentation.activity.Pre29VisibleScreenLifecycleBinding;
import io.opentelemetry.rum.internal.instrumentation.activity.RumFragmentActivityRegisterer;
import io.opentelemetry.rum.internal.instrumentation.activity.VisibleScreenLifecycleBinding;
import io.opentelemetry.rum.internal.instrumentation.activity.VisibleScreenTracker;
import io.opentelemetry.rum.internal.instrumentation.fragment.RumFragmentLifecycleCallbacks;
import io.opentelemetry.rum.internal.instrumentation.startup.AppStartupTimer;
import java.util.function.Function;

/**
* This is an umbrella instrumentation that covers several things: * startup timer callback is
* registered so that UI startup time can be measured - activity lifecycle callbacks are registered
* so that lifecycle events can be generated - activity lifecycle callback listener is registered to
* that will register a FragmentLifecycleCallbacks when appropriate - activity lifecycle callback
* listener is registered to dispatch events to the VisibleScreenTracker
*/
public class AndroidLifecycleInstrumentation {

private static final String INSTRUMENTATION_SCOPE = "io.opentelemetry.lifecycle";
private final AppStartupTimer startupTimer;
private final VisibleScreenTracker visibleScreenTracker;

private final Function<Tracer, Tracer> tracerCustomizer;

AndroidLifecycleInstrumentation(AndroidLifecycleInstrumentationBuilder builder) {
this.startupTimer = builder.startupTimer;
this.visibleScreenTracker = builder.visibleScreenTracker;
this.tracerCustomizer = builder.tracerCustomizer;
}

public static AndroidLifecycleInstrumentationBuilder builder() {
return new AndroidLifecycleInstrumentationBuilder();
}

public void installOn(InstrumentedApplication app) {
installStartupTimerInstrumentation(app);
installActivityLifecycleEventsInstrumentation(app);
installFragmentLifecycleInstrumentation(app);
installScreenTrackingInstrumentation(app);
}

private void installStartupTimerInstrumentation(InstrumentedApplication app) {
app.getApplication()
.registerActivityLifecycleCallbacks(startupTimer.createLifecycleCallback());
}

private void installActivityLifecycleEventsInstrumentation(InstrumentedApplication app) {
Application.ActivityLifecycleCallbacks activityCallbacks = buildActivityEventsCallback(app);
app.getApplication().registerActivityLifecycleCallbacks(activityCallbacks);
}

@NonNull
private Application.ActivityLifecycleCallbacks buildActivityEventsCallback(
InstrumentedApplication instrumentedApp) {
Tracer delegateTracer =
instrumentedApp.getOpenTelemetrySdk().getTracer(INSTRUMENTATION_SCOPE);
Tracer tracer = tracerCustomizer.apply(delegateTracer);

ActivityTracerCache tracers =
new ActivityTracerCache(tracer, visibleScreenTracker, startupTimer);
if (Build.VERSION.SDK_INT < 29) {
return new Pre29ActivityCallbacks(tracers);
}
return new ActivityCallbacks(tracers);
}

private void installFragmentLifecycleInstrumentation(InstrumentedApplication app) {
Application.ActivityLifecycleCallbacks fragmentRegisterer = buildFragmentRegisterer(app);
app.getApplication().registerActivityLifecycleCallbacks(fragmentRegisterer);
}

@NonNull
private Application.ActivityLifecycleCallbacks buildFragmentRegisterer(
InstrumentedApplication app) {

Tracer delegateTracer = app.getOpenTelemetrySdk().getTracer(INSTRUMENTATION_SCOPE);
Tracer tracer = tracerCustomizer.apply(delegateTracer);
RumFragmentLifecycleCallbacks fragmentLifecycle =
new RumFragmentLifecycleCallbacks(tracer, visibleScreenTracker);
if (Build.VERSION.SDK_INT < 29) {
return RumFragmentActivityRegisterer.createPre29(fragmentLifecycle);
}
return RumFragmentActivityRegisterer.create(fragmentLifecycle);
}

private void installScreenTrackingInstrumentation(InstrumentedApplication app) {
Application.ActivityLifecycleCallbacks screenTrackingBinding =
buildScreenTrackingBinding(visibleScreenTracker);
app.getApplication().registerActivityLifecycleCallbacks(screenTrackingBinding);
}

@NonNull
private Application.ActivityLifecycleCallbacks buildScreenTrackingBinding(
VisibleScreenTracker visibleScreenTracker) {
if (Build.VERSION.SDK_INT < 29) {
return new Pre29VisibleScreenLifecycleBinding(visibleScreenTracker);
}
return new VisibleScreenLifecycleBinding(visibleScreenTracker);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.instrumentation.lifecycle;

import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.rum.internal.instrumentation.activity.VisibleScreenTracker;
import io.opentelemetry.rum.internal.instrumentation.startup.AppStartupTimer;
import java.util.function.Function;

public class AndroidLifecycleInstrumentationBuilder {
AppStartupTimer startupTimer;
VisibleScreenTracker visibleScreenTracker;
Function<Tracer, Tracer> tracerCustomizer = Function.identity();

public AndroidLifecycleInstrumentationBuilder setStartupTimer(AppStartupTimer timer) {
this.startupTimer = timer;
return this;
}

public AndroidLifecycleInstrumentationBuilder setVisibleScreenTracker(
VisibleScreenTracker tracker) {
this.visibleScreenTracker = tracker;
return this;
}

public AndroidLifecycleInstrumentationBuilder setTracerCustomizer(
Function<Tracer, Tracer> customizer) {
this.tracerCustomizer = customizer;
return this;
}

public AndroidLifecycleInstrumentation build() {
return new AndroidLifecycleInstrumentation(this);
}
}

0 comments on commit a49c644

Please sign in to comment.