AWS Distro for OpenTelemetry
Manual Instrumentation for Traces and RUM Events with the Android SDK
Manual Instrumentation for Traces and RUM Events with the Android SDK
Introduction
The AWS Distro for OpenTelemetry (ADOT) Android SDK can used with any Android application running on Android OS 8+ (API level 26+) to gather real user monitoring (RUM) telemetry data. The Android SDK acts as a wrapper atop the OpenTelemetry (OTel) SDK to simplify the process of instrumenting applications. It is pre-configured for compatibility with AWS CloudWatch RUM but can also be used with any other tracing backend. Out of the box, it propagates traces using W3C Trace Context.
The Android agent is the recommended way to instrument most Android applications for Traces and RUM Events. For more information, refer to the documentation on auto-instrumentation.
Requirements
Currently, the Android SDK is only officially supported on applications running on Android 8.0 Oreo (API version 26) or greater. Java 8 (or later) and Kotlin 1.8.0 (or later) is required to build the library.
Installation
The ADOT Android SDK is published to Maven Central as the core artifact. You should add the dependency to your application's build.gradle . For example (using Kotlin DSL):
1plugins {2 id("com.android.application")3 id("org.jetbrains.kotlin.android")4}5
6dependencies {7 // ADOT Android Core - for manual instrumentation8 implementation("software.amazon.opentelemetry.android:core:1.0.0")9 10 // Optional: Auth module for Sigv4 authentication11 implementation("software.amazon.opentelemetry.android:kotlin-sdk-auth:1.0.0")12
13 // Automated HTTP client instrumentation with ByteBuddy (optional but recommended)14 byteBuddy("io.opentelemetry.android.instrumentation:okhttp3-agent:0.15.0-alpha")15 byteBuddy("io.opentelemetry.android.instrumentation:httpurlconnection-agent:0.15.0-alpha")16}Adding the core module as a dependency requires you to manually initialize the SDK in your application code. Next, we'll discuss how to initialize and configure the SDK.
Initializing the SDK in your application
In your application, preferably in Application.onCreate(), you will need to initialize the SDK. At minimum, this requires the following:
1import software.amazon.opentelemetry.android.OpenTelemetryRumClient2
3class MyApplication : Application() {4 override fun onCreate() {5 super.onCreate()6
7 OpenTelemetryRumClient {8 androidApplication = this@MyApplication9 }10 }11}This will initialize the OTel SDKs with the pre-enabled suite of default instrumentations on your provided androidApplication. Next, you will configure the SDK to point the telemetry somewhere.
NOTE: If you don't already have an Application class defined, you will need to register this new MyApplication class in your Android manifest. You can find that in your AndroidManifest.xml file. For example:
1<application>2 android:name="com.example.MyApplication"3
4 <!-- All other attributes, activities, etc -->5 6 android:label="MyApplication"7
8</application>Configuring the SDK with AWS CloudWatch RUM (with resource-based policies)
ADOT Android is pre-configured to work with AWS CloudWatch RUM. To export RUM telemetry to CloudWatch RUM, you will need to first create an app monitor with resource-based policies. Next, modify your code with the following:
1import io.opentelemetry.sdk.resources.Resource2import software.amazon.opentelemetry.android.OpenTelemetryRumClient3
4class MyApplication : Application() {5 override fun onCreate() {6 super.onCreate()7
8 OpenTelemetryRumClient {9 androidApplication = this@MyApplication 10 11 // point telemetry to AWS RUM12 awsRum {13 region = "<your region>"14 appMonitorId = "<your app monitor id>"15 16 // optional, if you have a resource-based policy with an alias17 alias = "<your rum alias>"18 }19 20 // customize your OTel Resource21 otelResource = Resource.builder()22 .put("service.name", "MyApplication")23 .put("service.namespace", "MyTeam")24 .put("service.version", "1.0.0")25 .put("deployment.environment", "production")26 .build()27 }28 }29}That's it! This is the minimum configuration to begin exporting RUM telemetry to CloudWatch RUM. From there, you can use CloudWatch RUM and CloudWatch Application Signals to monitor your application health and track long-term application performance against your business objectives.
Application Signals provides you with a unified, application-centric view of your applications, services, and dependencies, and helps you monitor and triage application health.
Get started with CloudWatch Application Signals
Use resource-based policies with CloudWatch RUM
Configuring the SDK with other OTLP endpoints
To export to a third-party OTLP endpoint that is not yet supported in the Android SDK out of the box, you can configure custom Span and LogRecord exporters:
1import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter2import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter3import io.opentelemetry.sdk.resources.Resource4
5class MyApplication : Application() {6 override fun onCreate() {7 super.onCreate()8
9 OpenTelemetryRumClient {10 androidApplication = this@MyApplication11 12 // Configure custom OTLP endpoints13 spanExporter = OtlpHttpSpanExporter.builder()14 .setEndpoint("https://your-collector.example.com/v1/traces")15 .build()16 17 logRecordExporter = OtlpHttpLogRecordExporter.builder()18 .setEndpoint("https://your-collector.example.com/v1/logs")19 .build()20 21 otelResource = Resource.builder()22 .put("service.name", "MyApplication")23 .put("service.version", "1.0.0")24 .build()25 }26 }27}Adding custom headers
If your OTLP endpoint requires authentication or custom headers, you can supply those yourself. For example:
1spanExporter = OtlpHttpSpanExporter.builder()2 .setEndpoint("https://your-collector.example.com/v1/traces")3 .setHeaders(mapOf(4 "Authorization" to "Bearer your-token",5 "X-Custom-Header" to "value"6 ))7 .build()Exporting with Sigv4 headers
To simplify the process of exporting telemetry via OTLP with Sigv4 signed requests, the Android SDK ships with an optional kotlin-sdk-auth library. You are responsible for sourcing your own credentials in a secure manner. An example implementation could look like:
1import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials2import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider3import io.opentelemetry.sdk.resources.Resource4import software.amazon.opentelemetry.android.OpenTelemetryRumClient5import software.amazon.opentelemetry.android.auth.AwsSigV4SpanExporter6import software.amazon.opentelemetry.android.auth.AwsSigV4LogRecordExporter7
8class MyApplication : Application() {9 override fun onCreate() {10 super.onCreate()11
12 // Configure your AWS credentials provider13 val credentialsProvider = CredentialsProvider {14 Credentials(15 accessKeyId = "your-access-key-id",16 secretAccessKey = "your-secret-access-key"17 sessionToken = "your-session-token"18 )19 }20
21 OpenTelemetryRumClient {22 androidApplication = this@MyApplication23 24 // Configure SigV4-signed exporters for RUM data plane25 spanExporter = AwsSigV4SpanExporter.builder()26 .setEndpoint("https://dataplane.rum.us-east-1.amazonaws.com/v1/rum")27 .setRegion("us-east-1")28 .setServiceName("rum")29 .setCredentialsProvider(credentialsProvider)30 .build()31 32 logRecordExporter = AwsSigV4LogRecordExporter.builder()33 .setEndpoint("https://dataplane.rum.us-east-1.amazonaws.com/v1/rum")34 .setRegion("us-east-1")35 .setServiceName("rum")36 .setCredentialsProvider(credentialsProvider)37 .build()38 39 otelResource = Resource.builder()40 .put("service.name", "MyApplication")41 .put("service.version", "1.0.0")42 .build()43 }44 }45}NOTE: Vending your application with static credentials in a production environment carries inherent risk. One possible solution is to source your credentials from Amazon Cognito using the AWS SDK for Kotlin.
Advanced customization of the SDK
By default, the SDK enables all available telemetry types. You can selectively enable or disable specific telemetry types to control what RUM data is collected from your application.
1import io.opentelemetry.sdk.resources.Resource2import io.opentelemetry.sdk.trace.samplers.Sampler3import software.amazon.opentelemetry.android.OpenTelemetryRumClient4import software.amazon.opentelemetry.android.TelemetryConfig5
6class MyApplication : Application() {7 override fun onCreate() {8 super.onCreate()9
10 OpenTelemetryRumClient {11 androidApplication = this@MyApplication12 13 awsRum {14 region = "us-east-1"15 appMonitorId = "your-app-monitor-id"16 }17 18 // Customize telemetry types19 telemetry = listOf(20 TelemetryConfig.ACTIVITY, // Activity lifecycle spans21 TelemetryConfig.FRAGMENT, // Fragment lifecycle spans22 TelemetryConfig.ANR, // ANR detection23 TelemetryConfig.CRASH, // Crash reporting24 TelemetryConfig.NETWORK, // Network state monitoring25 TelemetryConfig.SLOW_RENDERING, // Slow rendering detection26 TelemetryConfig.STARTUP, // App startup monitoring27 TelemetryConfig.HTTP_URLCONNECTION, // HttpURLConnection instrumentation28 TelemetryConfig.OKHTTP_3, // OkHttp3 instrumentation29 TelemetryConfig.UI_LOADING, // UI load time (TTFD)30 TelemetryConfig.SESSION_EVENTS // Session start/end events31 )32 33 otelResource = Resource.builder()34 .put("service.name", "MyApplication")35 .build()36
37 tracerSampler = Sampler.create(0.1) // sample 10% of traces38 }39 }40}For more advanced configuration options, refer to the ADOT Android GitHub repository.