diff --git a/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java b/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java index a18d68605..096a9602b 100644 --- a/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java +++ b/instrumentation/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java @@ -8,14 +8,21 @@ import static java.util.Objects.requireNonNull; import android.app.Application; +import android.content.Context; +import android.os.Looper; import android.util.Log; import io.opentelemetry.android.config.DiskBufferingConfiguration; import io.opentelemetry.android.config.OtelRumConfig; import io.opentelemetry.android.instrumentation.InstrumentedApplication; 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.crash.CrashReporter; +import io.opentelemetry.android.instrumentation.crash.CrashReporterBuilder; import io.opentelemetry.android.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.android.instrumentation.network.NetworkAttributesSpanAppender; import io.opentelemetry.android.instrumentation.network.NetworkChangeMonitor; +import io.opentelemetry.android.instrumentation.slowrendering.SlowRenderingDetector; import io.opentelemetry.android.instrumentation.startup.InitializationEvents; import io.opentelemetry.android.instrumentation.startup.SdkInitializationEvents; import io.opentelemetry.android.internal.features.persistence.DiskManager; @@ -76,6 +83,8 @@ public final class OpenTelemetryRumBuilder { private Resource resource; @Nullable private CurrentNetworkProvider currentNetworkProvider = null; private InitializationEvents initializationEvents = InitializationEvents.NO_OP; + private Consumer anrCustomizer = x -> {}; + private Consumer crashReporterCustomizer = x -> {}; private static TextMapPropagator buildDefaultPropagator() { return TextMapPropagator.composite( @@ -145,6 +154,34 @@ 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 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 + * effectively a no-op. + * + * @param customizer A Consumer that will recieve the {@link CrashReporterBuilder} before the + * {@link CrashReporter} is built. + * @return this. + */ + public OpenTelemetryRumBuilder addCrashReportingCustomization( + Consumer customizer) { + this.crashReporterCustomizer = customizer; + return this; + } + /** * Adds a {@link BiFunction} to invoke the with the {@link SdkMeterProviderBuilder} to allow * customization. The return value of the {@link BiFunction} will replace the passed-in @@ -317,6 +354,49 @@ private void applyConfiguration() { return tracerProviderBuilder.addSpanProcessor(screenAttributesAppender); }); } + + // 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 slow rendering detection if enabled + if (config.isSlowRenderingDetectionEnabled()) { + addInstrumentation( + instrumentedApplication -> { + SlowRenderingDetector.builder() + .setSlowRenderingDetectionPollInterval( + config.getSlowRenderingDetectionPollInterval()) + .build() + .installOn(instrumentedApplication); + initializationEvents.slowRenderingDetectorInitialized(); + }); + } + + // Enable crash reporting instrumentation + if (config.isCrashReportingEnabled()) { + addInstrumentation( + instrumentedApplication -> { + Context context = + instrumentedApplication.getApplication().getApplicationContext(); + CrashReporterBuilder builder = + CrashReporter.builder() + .addAttributesExtractor( + RuntimeDetailsExtractor.create(context)); + crashReporterCustomizer.accept(builder); + builder.build().installOn(instrumentedApplication); + + initializationEvents.crashReportingInitialized(); + }); + } } private CurrentNetworkProvider getOrCreateCurrentNetworkProvider() { diff --git a/instrumentation/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java b/instrumentation/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java index 7051c79db..fda468f44 100644 --- a/instrumentation/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java +++ b/instrumentation/src/main/java/io/opentelemetry/android/config/OtelRumConfig.java @@ -8,6 +8,7 @@ import io.opentelemetry.android.ScreenAttributesSpanProcessor; import io.opentelemetry.android.instrumentation.network.CurrentNetworkProvider; import io.opentelemetry.api.common.Attributes; +import java.time.Duration; import java.util.function.Supplier; /** @@ -17,6 +18,9 @@ */ public class OtelRumConfig { + private static final Duration DEFAULT_SLOW_RENDERING_DETECTION_POLL_INTERVAL = + Duration.ofSeconds(1); + private Supplier globalAttributesSupplier = Attributes::empty; private boolean includeNetworkAttributes = true; private boolean generateSdkInitializationEvents = true; @@ -24,6 +28,11 @@ public class OtelRumConfig { private DiskBufferingConfiguration diskBufferingConfiguration = DiskBufferingConfiguration.builder().build(); private boolean networkChangeMonitoringEnabled = true; + private boolean anrDetectionEnabled = true; + private boolean slowRenderingDetectionEnabled = true; + private Duration slowRenderingDetectionPollInterval = + DEFAULT_SLOW_RENDERING_DETECTION_POLL_INTERVAL; + private boolean crashReportingEnabled = true; /** * Configures the set of global attributes to emit with every span and event. Any existing @@ -70,7 +79,7 @@ public boolean shouldIncludeNetworkAttributes() { * @return this */ public OtelRumConfig disableSdkInitializationEvents() { - this.generateSdkInitializationEvents = false; + generateSdkInitializationEvents = false; return this; } @@ -86,7 +95,7 @@ public boolean shouldGenerateSdkInitializationEvents() { * @return this */ public OtelRumConfig disableScreenAttributes() { - this.includeScreenAttributes = false; + includeScreenAttributes = false; return this; } @@ -99,24 +108,84 @@ public DiskBufferingConfiguration getDiskBufferingConfiguration() { return diskBufferingConfiguration; } - /** Sets the parameters for caching signals in disk in order to export them later. */ - public void setDiskBufferingConfiguration( + /** + * 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; } /** * Sets the configuration so that network change monitoring, which is enabled by default, will * not be started. */ - public void disableNetworkChangeMonitoring() { - this.networkChangeMonitoringEnabled = false; + public OtelRumConfig disableNetworkChangeMonitoring() { + networkChangeMonitoringEnabled = false; + return this; + } + + /** Returns true if network change monitoring is enabled (default = true). */ + public boolean isNetworkChangeMonitoringEnabled() { + return networkChangeMonitoringEnabled; + } + + /** Returns true if ANR (application not responding) detection is enabled (default = true). */ + public boolean isAnrDetectionEnabled() { + return anrDetectionEnabled; } /** - * @return true if network change monitoring is enabled (default). + * Call this method to disable ANR (application not responding) detection. + * + * @return this */ - public boolean isNetworkChangeMonitoringEnabled() { - return this.networkChangeMonitoringEnabled; + public OtelRumConfig disableAnrDetection() { + anrDetectionEnabled = false; + return this; + } + + /** Returns true if the slow rendering detection instrumentation is enabled. */ + public boolean isSlowRenderingDetectionEnabled() { + return slowRenderingDetectionEnabled; + } + + /** + * Call this method to disable the slow rendering detection instrumentation. + * + * @return this + */ + public OtelRumConfig disableSlowRenderingDetection() { + slowRenderingDetectionEnabled = false; + return this; + } + + /** Returns the Duration at which slow renders are polled. Default = 1s. */ + public Duration getSlowRenderingDetectionPollInterval() { + return slowRenderingDetectionPollInterval; + } + + /** + * Call this to configure the duration for polling for slow renders. + * + * @return this + */ + public OtelRumConfig setSlowRenderingDetectionPollInterval(Duration duration) { + slowRenderingDetectionPollInterval = duration; + return this; + } + + /** Returns true if crash reporting is enabled. */ + public boolean isCrashReportingEnabled() { + return crashReportingEnabled; + } + + /** Call this method to disable crash reporting. */ + public OtelRumConfig disableCrashReporting() { + crashReportingEnabled = false; + return this; } } diff --git a/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/InitializationEvents.java b/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/InitializationEvents.java index fc8c3e476..acda6b4fa 100644 --- a/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/InitializationEvents.java +++ b/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/InitializationEvents.java @@ -17,6 +17,12 @@ public interface InitializationEvents { void networkMonitorInitialized(); + void anrMonitorInitialized(); + + void slowRenderingDetectorInitialized(); + + void crashReportingInitialized(); + InitializationEvents NO_OP = new InitializationEvents() { @Override @@ -30,5 +36,14 @@ public void currentNetworkProviderInitialized() {} @Override public void networkMonitorInitialized() {} + + @Override + public void anrMonitorInitialized() {} + + @Override + public void slowRenderingDetectorInitialized() {} + + @Override + public void crashReportingInitialized() {} }; } diff --git a/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.java b/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.java index e8730c975..21dd6c669 100644 --- a/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.java +++ b/instrumentation/src/main/java/io/opentelemetry/android/instrumentation/startup/SdkInitializationEvents.java @@ -26,6 +26,21 @@ public void currentNetworkProviderInitialized() { @Override public void networkMonitorInitialized() { - // TOOD: Build me "networkMonitorInitialized" + // TODO: Build me "networkMonitorInitialized" + } + + @Override + public void anrMonitorInitialized() { + // TODO: Build me "anrMonitorInitialized" + } + + @Override + public void slowRenderingDetectorInitialized() { + // TODO: Build me "slowRenderingDetectorInitialized" + } + + @Override + public void crashReportingInitialized() { + // TODO: Build me "crashReportingInitialized" } } diff --git a/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java b/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java index af8540c8b..97af61d14 100644 --- a/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java +++ b/instrumentation/src/test/java/io/opentelemetry/android/OpenTelemetryRumBuilderTest.java @@ -21,6 +21,7 @@ import android.app.Activity; import android.app.Application; +import android.os.Looper; import androidx.annotation.NonNull; import io.opentelemetry.android.config.DiskBufferingConfiguration; import io.opentelemetry.android.config.OtelRumConfig; @@ -61,6 +62,7 @@ class OpenTelemetryRumBuilderTest { final InMemorySpanExporter spanExporter = InMemorySpanExporter.create(); @Mock Application application; + @Mock Looper looper; @Mock android.content.Context applicationContext; @Mock Activity activity; @Mock ApplicationStateListener listener; @@ -70,6 +72,7 @@ class OpenTelemetryRumBuilderTest { @BeforeEach void setup() { when(application.getApplicationContext()).thenReturn(applicationContext); + when(application.getMainLooper()).thenReturn(looper); } @Test