After you instrument your application and report trace data to Managed Service for OpenTelemetry, Managed Service for OpenTelemetry starts monitoring your application. You can then view monitoring data, such as application topology, traces, and analyses of errors, slow transactions, and SQL. This topic describes how to instrument an Android application with OpenTelemetry and report its data.
Prerequisites
Demo
This topic demonstrates how to use OpenTelemetry to report trace data from an Android application. This method applies to both Java and Kotlin applications.
Sample code repository: opentelemetry-android-demo
Step 1: Create and configure an application
-
Create an application.
-
In Android Studio, create a new application, select the Basic Views Activity template, and then click Next.
-
Select Java or Kotlin as the language. Set the minimum SDK version to API 24: Android 7.0 (Nougat), and then click Finish.
-
-
Add dependencies.
In the module-level or project-level build.gradle file, add the following dependencies.
This example uses OpenTelemetry Java SDK version 1.25.0. For other versions, see Opentelemetry Java Releases. For the complete build.gradle sample code, see build.gradle.
implementation platform('io.opentelemetry:opentelemetry-bom:1.25.0') implementation "io.opentelemetry:opentelemetry-api" implementation "io.opentelemetry:opentelemetry-context" implementation 'io.opentelemetry:opentelemetry-exporter-otlp' implementation 'io.opentelemetry:opentelemetry-exporter-logging' implementation 'io.opentelemetry:opentelemetry-extension-kotlin' implementation 'io.opentelemetry:opentelemetry-sdk' implementation 'io.opentelemetry:opentelemetry-semconv' -
Configure network settings.
-
In the
app/res/xmldirectory, create a file named network_security_config.xml and add the following content.<!-- For the complete file content, see https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/res/xml/network_security_config.xml --> <?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <!-- Replace the following domain with the endpoint obtained in the Prerequisites section. Do not include "http://", the port number, or the URL path. --> <domain includeSubdomains="true">tracing-analysis-dc-hz.aliyuncs.com</domain> </domain-config> </network-security-config> -
Modify the
app/src/main/AndroidManifest.xmlfile and add the following two lines to enable network permissions for your application.<!-- For the complete file content, see https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/AndroidManifest.xml --> <?xml version="1.0" encoding="utf-8"?> <manifest ...> <!-- Add the following line to enable network permissions. --> <uses-permission android:name="android.permission.INTERNET" /> <application ... <!-- Add the following line to apply the network configuration for the data reporting domain. --> android:networkSecurityConfig="@xml/network_security_config" ...> ... </application> </manifest>
-
Step 2: Initialize OpenTelemetry
-
Create an OpenTelemetry utility class.
In the same directory as MainActivity, create an OpenTelemetryUtil file and add the following content.
-
Option 1: Report trace data over gRPC
/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/OpenTelemetryUtil.java */ Resource otelResource = Resource.getDefault().merge( Resource.create( Attributes.of( // Replace <your-service-name> with your application name. ResourceAttributes.SERVICE_NAME, "<your-service-name>", // Replace <your-host-name> with your host name. ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Prints trace data to logs or the command line. Comment out this line if not needed. // Replace <gRPC-endpoint> with the endpoint and <gRPC-token> with the authentication token that you obtained in the Prerequisites section. .addSpanProcessor(BatchSpanProcessor.builder( OtlpGrpcSpanExporter.builder() .setEndpoint("<gRPC-endpoint>") // Example: http://tracing-analysis-dc-hz.aliyuncs.com:8090 .addHeader("Authentication", "<gRPC-token>") // Example: xxxx@xxxx_xxxx@xxxx .build()).build() ) .setResource(otelResource) .build(); OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); // Get a tracer to create spans. tracer = openTelemetry.getTracer("android-tracer", "1.0.0");/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/OpenTelemetryUtil.kt */ val otelResource = Resource.getDefault().merge( Resource.create( Attributes.of( ResourceAttributes.SERVICE_NAME, "<your-service-name>", // Replace <your-service-name> with your application name. ResourceAttributes.HOST_NAME, "<your-host-name>" // Replace <your-host-name> with your host name. ) ) ) /* Report trace data over gRPC. */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Prints trace data to logs or the command line. Comment out this line if not needed. // Replace <gRPC-endpoint> with the endpoint and <gRPC-token> with the authentication token that you obtained in the Prerequisites section. .addSpanProcessor( BatchSpanProcessor.builder( OtlpGrpcSpanExporter.builder() .setEndpoint("<gRPC-endpoint>") // Example: http://tracing-analysis-dc-hz.aliyuncs.com:8090 .addHeader("Authentication", "<gRPC-token>") // Example: xxxx@xxxx_xxxx@xxxx .build() ).build() ) .setResource(otelResource) .build() val openTelemetry: OpenTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal() // Get a tracer to create spans. tracer = openTelemetry.getTracer("android-tracer", "1.0.0") -
Option 2: Report trace data over HTTP
/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/OpenTelemetryUtil.java */ Resource otelResource = Resource.getDefault().merge( Resource.create( Attributes.of( // Replace <your-service-name> with your application name. ResourceAttributes.SERVICE_NAME, "<your-service-name>", // Replace <your-host-name> with your host name. ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Prints trace data to logs or the command line. Comment out this line if not needed. // Replace <HTTP-endpoint> with the endpoint that you obtained in the Prerequisites section. .addSpanProcessor(BatchSpanProcessor.builder( OtlpHttpSpanExporter.builder() .setEndpoint("<HTTP-endpoint>") // Example: http://tracing-analysis-dc-hz.aliyuncs.com/adapt_xxxx@xxxx_xxxx@xxxx/api/otlp/traces .build()).build() ) .setResource(otelResource) .build(); OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); // Get a tracer to create spans. tracer = openTelemetry.getTracer("android-tracer", "1.0.0");/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/OpenTelemetryUtil.kt */ val otelResource = Resource.getDefault().merge( Resource.create( Attributes.of( ResourceAttributes.SERVICE_NAME, "<your-service-name>", // Replace <your-service-name> with your application name. ResourceAttributes.HOST_NAME, "<your-host-name>" // Replace <your-host-name> with your host name. ) ) ) /* Report trace data over HTTP. */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Prints trace data to logs or the command line. Comment out this line if not needed. // Replace <HTTP-endpoint> with the endpoint that you obtained in the Prerequisites section. .addSpanProcessor(BatchSpanProcessor.builder( OtlpHttpSpanExporter.builder() .setEndpoint("<HTTP-endpoint>") // Example: http://tracing-analysis-dc-hz.aliyuncs.com/adapt_xxxx@xxxx_xxxx@xxxx/api/otlp/traces .build()).build() ) .setResource(otelResource) .build(); val openTelemetry: OpenTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal() // Get a tracer to create spans. tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
-
-
Initialize OpenTelemetry when the application starts.
Call the OpenTelemetryUtil.init() method in the
onCreatemethod of MainActivity./** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/MainActivity.java */ ... public class MainActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Add this line to initialize OpenTelemetry. OpenTelemetryUtil.init(); ... } ... }/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/MainActivity.kt */ ... class MainActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?) { WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) // Add this line to initialize OpenTelemetry. OpenTelemetryUtil.init() ... } }
Step 3: Create spans to track traces
-
Create a span.
In the button click event listener of the
FirstFragmentfile, create a span named First Fragment Button onClick./** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ public void onClick(View view) { // Get the tracer. Tracer tracer = OpenTelemetryUtil.getTracer(); // Create a span. Span span = tracer.spanBuilder("First Fragment Button onClick").startSpan(); try (Scope scope = span.makeCurrent()) { // Get the traceId. System.out.println(span.getSpanContext().getTraceId()); ... } finally { span.end(); } }/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ binding.buttonFirst.setOnClickListener { // Get the tracer. val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // Create a span. val span = tracer.spanBuilder("First Fragment Button onClick").startSpan() try { span.makeCurrent().use { scope -> // Get the traceId. println(span.spanContext.traceId) // Get the spanId. println(span.spanContext.spanId) findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) } } catch (t: Throwable) { span.setStatus(StatusCode.ERROR, "Something wrong in onClick") throw t } finally { span.end() } } -
Set attributes and events for the span.
/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ // Set an attribute. span.setAttribute("key", "value"); Attributes eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L); // Add an event. span.addEvent("onClick", eventAttributes);/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ // Set an attribute. span.setAttribute("key", "value") val eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L ) // Add an event. span.addEvent("onClick", eventAttributes) -
Set the status for the span.
/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ ... try (Scope scope = span.makeCurrent()) { ... } catch (Throwable t) { // Set the span status. span.setStatus(StatusCode.ERROR, "Something wrong in onClick"); throw t; } finally { span.end(); }/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ ... try { ... } catch (t: Throwable) { // Set the span status. span.setStatus(StatusCode.ERROR, "Something wrong in onClick") throw t } finally { span.end() } -
Create nested spans.
/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ public void parentSpan() { // Get the tracer. Tracer tracer = OpenTelemetryUtil.getTracer(); // Create a span. Span span = tracer.spanBuilder("Parent Span").startSpan(); try (Scope scope = span.makeCurrent()) { // Get the traceId. System.out.println(span.getSpanContext().getTraceId()); // Get the spanId. System.out.println(span.getSpanContext().getSpanId()); childSpan(); } finally { span.end(); } } public void childSpan() { // Get the tracer. Tracer tracer = OpenTelemetryUtil.getTracer(); // Create a span. Span span = tracer.spanBuilder("Child Span").startSpan(); try (Scope scope = span.makeCurrent()) { // Get the traceId. System.out.println(span.getSpanContext().getTraceId()); // Get the spanId. System.out.println(span.getSpanContext().getSpanId()); } finally { span.end(); } }/** For the complete code, visit the following link: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ // Nested span. fun parentSpan() { // Get the tracer. val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // Create a span. val span = tracer.spanBuilder("Parent Span").startSpan() try { span.makeCurrent().use { scope -> // Get the traceId. println(span.spanContext.traceId) // Get the spanId. println(span.spanContext.spanId) childSpan() } } finally { span.end() } } // Nested span. fun childSpan() { // Get the tracer. val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // Create a span. val span = tracer.spanBuilder("Child Span").startSpan() try { span.makeCurrent().use { scope -> // Get the traceId. println(span.spanContext.traceId) // Get the spanId. println(span.spanContext.spanId) } } finally { span.end() } }
Step 4: Run and view trace data
-
Run the project and click the button on the demo application page. After you run the project in Android Studio, the AndroidJavaDemo application starts in the emulator and displays the default First Fragment page, confirming that the project is configured correctly and running as expected.
Check the logs in Logcat. You can see the span information exported by the
LoggingSpanExporter.2023-xx-xx 15547-15547 System.out com.example.androidjavademo I 0c8b92db3d8b038ea3eedd0ec25a4abb 2023-xx-xx 15547-15547 System.out com.example.androidjavademo I 9ecd666297f5d8f6 2023-xx-xx 15547-15547 System.out com.example.androidjavademo I 0c8b92db3d8b038ea3eedd0ec25a4abb 2023-xx-xx 15547-15547 System.out com.example.androidjavademo I 7e7eaffe7bd23cb8 2023-xx-xx 15547-15547 System.out com.example.androidjavademo I 0c8b92db3d8b038ea3eedd0ec25a4abb 2023-xx-xx 15547-15547 System.out com.example.androidjavademo I 98ef17d962ecf4d1 2023-xx-xx 15547-15547 LoggingSpanExporter com.example.androidjavademo I 'Child Span' : 0c8b92db3d8b038ea3eedd0ec25a4abb 98ef17d962ecf4d1 INTERNAL [tracer: android-tracer:1.0.0] 2023-xx-xx 15547-15547 LoggingSpanExporter com.example.androidjavademo I 'Parent Span' : 0c8b92db3d8b038ea3eedd0ec25a4abb 7e7eaffe7bd23cb8 INTERNAL [tracer: android-tracer:1.0.0] 2023-xx-xx 15547-15547 LoggingSpanExporter com.example.androidjavademo I 'First Fragment Button onClick' : 0c8b92db3d8b038ea3eedd0ec25a4abb 9ecd666297f5d8f6 INTERNAL [tracer: android-tracer:1.0.0] 2023-xx-xx 15547-15585 EGL_emulation com.example.androidjavademo D app_time_stats: avg=460.19ms min=2.23ms max=3653.05ms count=8 -
Log on to the ARMS console. In the left-side navigation pane, choose . On the Applications page, click the name of the application. On the page that appears, view the trace data.
NoteIf the
icon is displayed in the Language column, the application is connected to Application Monitoring. If a hyphen (-) is displayed, the application is connected to Managed Service for OpenTelemetry. On the trace details page, the summary shows the duration, number of applications, trace depth, and total number of spans. The span list displays a three-level hierarchy: the root span First Fragment Button onClick (6.835 ms), the child span Parent Span (2.998 ms), and the grandchild span Child Span (0.14 ms). The application name for all of these spans is
otel-android-demo, and their statuses are normal.
Step 5: Correlate client and server traces
-
Modify the trace propagation format in the header.
-
Different protocols use different HTTP headers to propagate the trace context. For example, OpenTelemetry uses the W3C Trace Context format by default, while Zipkin uses the B3 or B3 Multi format. You can also change the default format. For more information about propagation formats, see Specify the format to pass trace data.
-
Based on your backend application's protocol, set the matching propagation format (
textPropagators) on the client to correlate traces between the Android client and the backend application.-
If the backend application uses the default OpenTelemetry W3C Trace Context format, you do not need to set
textPropagatorson the client. -
If the backend application uses Zipkin's B3 or B3 Multi format, the client's
textPropagatorsmust be set toB3Propagator.// Set the B3 propagation format. OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( B3Propagator.injectingMultiHeaders(), B3Propagator.injectingSingleHeader()))) .buildAndRegisterGlobal(); -
If the backend application uses the Jaeger protocol, the client's
textPropagatorsmust be set toJaegerPropagator.// Set the Jaeger propagation format. OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( JaegerPropagator.getInstance()))) .buildAndRegisterGlobal(); -
You can also set multiple trace propagation formats at the same time.
// Use W3C Trace Context, B3, and Jaeger trace propagation formats simultaneously. OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( W3CTraceContextPropagator.getInstance(), B3Propagator.injectingMultiHeaders(), B3Propagator.injectingSingleHeader() JaegerPropagator.getInstance()))) .buildAndRegisterGlobal();
-
-
-
Import okhttp3 and opentelemetry-okhttp.
opentelemetry-okhttp-3.0is an OpenTelemetry auto-instrumentation plugin for OkHttp that automatically intercepts all network requests sent through OkHttp3 and creates traces.-
Add the following two dependencies to
build.gradle.dependencies { ... implementation 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0:2.3.0-alpha' implementation 'com.squareup.okhttp3:okhttp:4.12.0' } -
Create an
OkHttpConfiguration.import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.okhttp.v3_0.OkHttpTelemetry; import okhttp3.Call; import okhttp3.OkHttpClient; public class OkHttpConfiguration { //Use this Call.Factory implementation for making standard http client calls. public Call.Factory createTracedClient(OpenTelemetry openTelemetry) { return OkHttpTelemetry.builder(openTelemetry).build().newCallFactory(createClient()); } //your configuration of the OkHttpClient goes here: private OkHttpClient createClient() { return new OkHttpClient.Builder().build(); } }
-
-
Use OkHttp3 to send a network request to the backend application.
private void callHttpService() throws IOException { Tracer tracer = OpenTelemetryUtil.getTracer(); // Create a span. Span span = tracer.spanBuilder("AsyncRequestZipkinServer").startSpan(); System.out.println("AsyncRequestZipkinServer TraceID: " + span.getSpanContext().getTraceId()); System.out.println("AsyncRequestZipkinServer SpanID: " + span.getSpanContext().getSpanId()); try (Scope scope = span.makeCurrent()) { // Make a network request, for example, using OkHttp. OkHttpConfiguration configuration = new OkHttpConfiguration(); Call.Factory tracedClient = configuration.createTracedClient(GlobalOpenTelemetry.get()); Request request = new Request.Builder().url("${Server_Address}").get().build(); Call call = tracedClient.newCall(request); try (Response response = call.execute()) { // Handle the response. String responseBody = response.body().string(); System.out.println(responseBody); } catch (IOException e) { // Handle errors. e.printStackTrace(); } } finally { span.end(); } } -
On the Trace Explorer page, view the correlated end-to-end trace connecting the client and backend applications.
In this example,
AsyncRequestZipkinServeris the Android application, andzipkin-demo-serveris the backend application.
