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

Instrumentation API part 5 #445

Merged
merged 2 commits into from
Jun 21, 2024
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
1 change: 0 additions & 1 deletion android-agent/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ android {
dependencies {
implementation(project(":common"))
implementation(project(":instrumentation:activity"))
implementation(project(":instrumentation:anr"))
implementation(project(":instrumentation:crash"))
implementation(libs.androidx.core)
implementation(libs.androidx.navigation.fragment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@

import android.app.Application;
import android.content.Context;
import android.os.Looper;
import android.util.Log;
import io.opentelemetry.android.common.RumConstants;
import io.opentelemetry.android.config.OtelRumConfig;
import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfiguration;
import io.opentelemetry.android.features.diskbuffering.SignalFromDiskExporter;
import io.opentelemetry.android.features.diskbuffering.scheduler.ExportScheduleHandler;
import io.opentelemetry.android.instrumentation.activity.VisibleScreenTracker;
import io.opentelemetry.android.instrumentation.anr.AnrDetector;
import io.opentelemetry.android.instrumentation.anr.AnrDetectorBuilder;
import io.opentelemetry.android.instrumentation.common.InstrumentedApplication;
import io.opentelemetry.android.instrumentation.crash.CrashReporter;
import io.opentelemetry.android.instrumentation.crash.CrashReporterBuilder;
Expand Down Expand Up @@ -98,7 +95,6 @@ public final class OpenTelemetryRumBuilder {

private Resource resource;
private InitializationEvents initializationEvents = InitializationEvents.NO_OP;
private Consumer<AnrDetectorBuilder> anrCustomizer = x -> {};
private Consumer<CrashReporterBuilder> crashReporterCustomizer = x -> {};

private static TextMapPropagator buildDefaultPropagator() {
Expand Down Expand Up @@ -158,19 +154,6 @@ public OpenTelemetryRumBuilder addTracerProviderCustomizer(
return this;
}

/**
* Pass a Consumer that will receive the AnrDetectorBuilder in order to perform additional
* specific customizations. If ANR detection is disabled, this method is effectively a no-op.
*
* @param customizer A Consumer that will receive the {@link AnrDetectorBuilder} before the
* {@link AnrDetector} is built.
* @return this.
*/
public OpenTelemetryRumBuilder addAnrCustomization(Consumer<AnrDetectorBuilder> customizer) {
this.anrCustomizer = customizer;
return this;
}

/**
* Pass a Consumer that will receive the CrashReporterBuilder in order to perform additional
* specific customizations. If crash reporting is disabled via config, this method is
Expand Down Expand Up @@ -461,19 +444,6 @@ private void applyConfiguration() {
});
}

// Add ANR detection if enabled
if (config.isAnrDetectionEnabled()) {
Looper mainLooper = application.getMainLooper();
addInstrumentation(
instrumentedApplication -> {
AnrDetectorBuilder builder =
AnrDetector.builder().setMainLooper(mainLooper);
anrCustomizer.accept(builder);
builder.build().installOn(instrumentedApplication);
initializationEvents.anrMonitorInitialized();
});
}

// Enable crash reporting instrumentation
if (config.isCrashReportingEnabled()) {
addInstrumentation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ public class OtelRumConfig {
private boolean includeScreenAttributes = true;
private DiskBufferingConfiguration diskBufferingConfiguration =
DiskBufferingConfiguration.builder().build();
private boolean anrDetectionEnabled = true;
private boolean crashReportingEnabled = true;
private Duration sessionTimeout = Duration.ofMinutes(15);

Expand Down Expand Up @@ -114,21 +113,6 @@ public OtelRumConfig setDiskBufferingConfiguration(
return this;
}

/** Returns true if ANR (application not responding) detection is enabled (default = true). */
public boolean isAnrDetectionEnabled() {
return anrDetectionEnabled;
}

/**
* Call this method to disable ANR (application not responding) detection.
*
* @return this
*/
public OtelRumConfig disableAnrDetection() {
anrDetectionEnabled = false;
return this;
}

/** Returns true if crash reporting is enabled. */
public boolean isCrashReportingEnabled() {
return crashReportingEnabled;
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/anr/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ android {
dependencies {
api(platform(libs.opentelemetry.platform))
api(libs.opentelemetry.api)
api(project(":instrumentation:common-api"))
api(project(":android-agent"))
implementation(libs.androidx.core)
implementation(libs.opentelemetry.semconv)
implementation(libs.opentelemetry.sdk)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import android.os.Handler;
import android.os.Looper;
import io.opentelemetry.android.instrumentation.common.InstrumentedApplication;
import io.opentelemetry.android.internal.services.applifecycle.AppLifecycleService;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
Expand All @@ -17,50 +17,44 @@

/** Entrypoint for installing the ANR (application not responding) detection instrumentation. */
public final class AnrDetector {

/** Returns a new {@link AnrDetector} with the default settings. */
public static AnrDetector create() {
return builder().build();
}

/** Returns a new {@link AnrDetectorBuilder}. */
public static AnrDetectorBuilder builder() {
return new AnrDetectorBuilder();
}

private final List<AttributesExtractor<StackTraceElement[], Void>> additionalExtractors;
private final Looper mainLooper;
private final ScheduledExecutorService scheduler;
private final AppLifecycleService appLifecycleService;
private final OpenTelemetry openTelemetry;

AnrDetector(AnrDetectorBuilder builder) {
this.additionalExtractors = builder.additionalExtractors;
this.mainLooper = builder.mainLooper;
this.scheduler = builder.scheduler;
AnrDetector(
List<AttributesExtractor<StackTraceElement[], Void>> additionalExtractors,
Looper mainLooper,
ScheduledExecutorService scheduler,
AppLifecycleService appLifecycleService,
OpenTelemetry openTelemetry) {
this.additionalExtractors = additionalExtractors;
this.mainLooper = mainLooper;
this.scheduler = scheduler;
this.appLifecycleService = appLifecycleService;
this.openTelemetry = openTelemetry;
}

/**
* Installs the ANR detection instrumentation on the given {@link InstrumentedApplication}.
* Starts the ANR detection instrumentation.
*
* <p>When the main thread is unresponsive for 5 seconds or more, an event including the main
* thread's stack trace will be reported to the RUM system.
*/
public void installOn(InstrumentedApplication instrumentedApplication) {
public void start() {
Handler uiHandler = new Handler(mainLooper);
AnrWatcher anrWatcher =
new AnrWatcher(
uiHandler,
mainLooper.getThread(),
buildAnrInstrumenter(instrumentedApplication.getOpenTelemetrySdk()));
new AnrWatcher(uiHandler, mainLooper.getThread(), buildAnrInstrumenter());

AnrDetectorToggler listener = new AnrDetectorToggler(anrWatcher, scheduler);
// call it manually the first time to enable the ANR detection
listener.onApplicationForegrounded();

instrumentedApplication.registerApplicationStateListener(listener);
appLifecycleService.registerListener(listener);
}

private Instrumenter<StackTraceElement[], Void> buildAnrInstrumenter(
OpenTelemetry openTelemetry) {
private Instrumenter<StackTraceElement[], Void> buildAnrInstrumenter() {
return Instrumenter.<StackTraceElement[], Void>builder(
openTelemetry, "io.opentelemetry.anr", stackTrace -> "ANR")
// it's always an error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package io.opentelemetry.android.instrumentation.anr;

import androidx.annotation.Nullable;
import io.opentelemetry.android.instrumentation.common.ApplicationStateListener;
import io.opentelemetry.android.internal.services.applifecycle.ApplicationStateListener;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,55 @@

package io.opentelemetry.android.instrumentation.anr;

import android.app.Application;
import android.os.Looper;
import androidx.annotation.NonNull;
import io.opentelemetry.android.OpenTelemetryRum;
import io.opentelemetry.android.instrumentation.AndroidInstrumentation;
import io.opentelemetry.android.internal.services.ServiceManager;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/** A builder of {@link AnrDetector}. */
public final class AnrDetectorBuilder {

AnrDetectorBuilder() {}
/** Entry point for {@link AnrDetector}. */
public final class AnrInstrumentation implements AndroidInstrumentation {

final List<AttributesExtractor<StackTraceElement[], Void>> additionalExtractors =
new ArrayList<>();
Looper mainLooper = Looper.getMainLooper();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

/** Adds an {@link AttributesExtractor} that will extract additional attributes. */
public AnrDetectorBuilder addAttributesExtractor(
public AnrInstrumentation addAttributesExtractor(
AttributesExtractor<StackTraceElement[], Void> extractor) {
additionalExtractors.add(extractor);
return this;
}

/** Sets a custom {@link Looper} to run on. Useful for testing. */
public AnrDetectorBuilder setMainLooper(Looper looper) {
public AnrInstrumentation setMainLooper(Looper looper) {
mainLooper = looper;
return this;
}

// visible for tests
AnrDetectorBuilder setScheduler(ScheduledExecutorService scheduler) {
AnrInstrumentation setScheduler(ScheduledExecutorService scheduler) {
this.scheduler = scheduler;
return this;
}

/** Returns a new {@link AnrDetector} with the settings of this {@link AnrDetectorBuilder}. */
public AnrDetector build() {
return new AnrDetector(this);
@Override
public void install(
@NonNull Application application, @NonNull OpenTelemetryRum openTelemetryRum) {
AnrDetector anrDetector =
new AnrDetector(
additionalExtractors,
mainLooper,
scheduler,
ServiceManager.get().getAppLifecycleService(),
openTelemetryRum.getOpenTelemetry());
anrDetector.start();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.Looper;
import io.opentelemetry.android.instrumentation.common.InstrumentedApplication;
import io.opentelemetry.android.internal.services.applifecycle.AppLifecycleService;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import java.util.Collections;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
Expand All @@ -27,27 +28,26 @@ class AnrDetectorTest {

@Mock Looper mainLooper;
@Mock ScheduledExecutorService scheduler;
@Mock InstrumentedApplication instrumentedApplication;
@Mock AppLifecycleService appLifecycleService;

@Test
void shouldInstallInstrumentation() {
when(instrumentedApplication.getOpenTelemetrySdk())
.thenReturn(OpenTelemetrySdk.builder().build());
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build();

AnrDetector anrDetector =
AnrDetector.builder()
.setMainLooper(mainLooper)
.setScheduler(scheduler)
.addAttributesExtractor(constant(stringKey("test.key"), "abc"))
.build();
anrDetector.installOn(instrumentedApplication);
new AnrDetector(
Collections.singletonList(constant(stringKey("test.key"), "abc")),
mainLooper,
scheduler,
appLifecycleService,
openTelemetry);
anrDetector.start();

// verify that the ANR scheduler was started
verify(scheduler)
.scheduleWithFixedDelay(
isA(AnrWatcher.class), eq(1L), eq(1L), eq(TimeUnit.SECONDS));
// verify that an application listener was installed
verify(instrumentedApplication)
.registerApplicationStateListener(isA(AnrDetectorToggler.class));
verify(appLifecycleService).registerListener(isA(AnrDetectorToggler.class));
}
}