After you use OpenTelemetry to instrument an application and report the trace data to Managed Service for OpenTelemetry, Managed Service for OpenTelemetry starts to monitor the application. You can view the monitoring data of the application such as application topology, traces, abnormal transactions, slow transactions, and SQL analysis. This topic describes how to instrument an Android application and report the trace data of the Android application by using OpenTelemetry.
Before you begin
Sample code
In this example, OpenTelemetry is used to report the trace data of an Android application. The method used in this example also applies to applications written in Java or Kotlin.
You can download the sample code from opentelemetry-android-demo.
Step 1: Create and configure an application
Create an application.
Create an application in Android Studio. Select Basic Views Activity and click Next.
Select Java or Kotlin as Language, select API 24: Android 7.0 (Nougat) as Minimum SDK, and then click Finish.
Add dependencies.
Add the following dependencies to the build.gradle file of a module or project.
In this example, the version of OpenTelemetry SDK for Java is 1.25.0. For information about the SDK versions, see Releases of opentelemetry-java. For information about the complete 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.
Create a file named network_security_config.xml in the
app/res/xml
directory and add the following content to the file:<!-- To view the complete content of the file, visit 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 domain name with the endpoint that you have obtained as instructed in the "Before you begin" section of this topic. The endpoint cannot contain http://, a port number, or a URL path. --> <domain includeSubdomains="true">tracing-analysis-dc-hz.aliyuncs.com</domain> </domain-config> </network-security-config>
Add the following content to the
app/src/main/AndroidManifest.xml
file to allow the application to access the network:<!-- To view the complete content of the file, visit 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 grant network access permissions. --> <uses-permission android:name="android.permission.INTERNET" /> <application ... <!-- Add the following line to configure the network for the domain name to which you want to report data. --> android:networkSecurityConfig="@xml/network_security_config" ...> ... </application> </manifest>
Step 2: Initialize OpenTelemetry
Create an OpenTelemetry utility class.
Create an OpenTelemetryUtil file in the directory in which the MainActivity file resides and add the following content to the OpenTelemetryUtil file.
Method 1: Report trace data over gRPC
/** Visit the following link to view the complete code: 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 hostname. ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Display the trace data in logs or the command line. Comment this line if you do not need it. // Replace <gRPC-endpoint> with the endpoint that you have obtained as instructed in the "Before you begin" section of this topic, and replace <gRPC-token> with the authentication token. .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(); // Obtain the tracer that is used to create a span. tracer = openTelemetry.getTracer("android-tracer", "1.0.0");
/** Visit the following link to view the complete code: 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 hostname. ) ) ) /* Report trace data over gRPC. */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Display the trace data in logs or the command line. Comment this line if you do not need it. // Replace <gRPC-endpoint> with the endpoint that you have obtained as instructed in the "Before you begin" section of this topic, and replace <gRPC-token> with the authentication token. .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() // Obtain the tracer that is used to create a span. tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
Method 2: Report trace data over HTTP
/** Visit the following link to view the complete code: 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 hostname. ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Display the trace data in logs or the command line. Comment this line if you do not need it. // Replace <HTTP-endpoint> with the endpoint that you have obtained as instructed in the "Before you begin" section of this topic. .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(); // Obtain the tracer that is used to create a span. tracer = openTelemetry.getTracer("android-tracer", "1.0.0");
/** Visit the following link to view the complete code: 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 hostname. ) ) ) /* Report trace data over HTTP. */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // Optional. Display the trace data in logs or the command line. Comment this line if you do not need it. // Replace <HTTP-endpoint> with the endpoint that you have obtained as instructed in the "Before you begin" section of this topic. .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() // Obtain the tracer that is used to create a span. tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
Initialize OpenTelemetry during the application initialization.
Call the OpenTelemetryUtil.init() method in the onCreate method of the MainActivity class.
/** Visit the following link to view the complete code: 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 the following line to initialize OpenTelemetry. OpenTelemetryUtil.init(); ... } ... }
/** Visit the following link to view the complete code: 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 the following line to initialize OpenTelemetry. OpenTelemetryUtil.init() ... } }
Step 3: Create a span to track trace data
Create a span.
In the method of monitoring button tap events in the FirstFragment file, create a span named First Fragment Button onClick.
/** Visit the following link to view the complete code: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ public void onClick(View view) { // Obtain the tracer. Tracer tracer = OpenTelemetryUtil.getTracer(); // Create a span. Span span = tracer.spanBuilder("First Fragment Button onClick").startSpan(); try (Scope scope = span.makeCurrent()) { // Obtain the trace ID. System.out.println(span.getSpanContext().getTraceId()); ... } finally { span.end(); } }
/** Visit the following link to view the complete code: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ binding.buttonFirst.setOnClickListener { // Obtain the tracer. val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // Create a span. val span = tracer.spanBuilder("First Fragment Button onClick").startSpan() try { span.makeCurrent().use { scope -> // Obtain the trace ID. println(span.spanContext.traceId) // Obtain the span ID. 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() } }
Configure attributes and an event for the span.
/** Visit the following link to view the complete code: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ // Configure attributes. span.setAttribute("key", "value"); Attributes eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L); // Add an event. span.addEvent("onClick", eventAttributes);
/** Visit the following link to view the complete code: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ // Configure attributes. span.setAttribute("key", "value") val eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L ) // Add an event. span.addEvent("onClick", eventAttributes)
Configure the status of the span.
/** Visit the following link to view the complete code: 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) { // Configure the status of the span. span.setStatus(StatusCode.ERROR, "Something wrong in onClick"); throw t; } finally { span.end(); }
/** Visit the following link to view the complete code: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ ... try { ... } catch (t: Throwable) { // Configure the status of the span. span.setStatus(StatusCode.ERROR, "Something wrong in onClick") throw t } finally { span.end() }
Create a nested span.
/** Visit the following link to view the complete code: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ public void parentSpan() { // Obtain the tracer. Tracer tracer = OpenTelemetryUtil.getTracer(); // Create a span. Span span = tracer.spanBuilder("Parent Span").startSpan(); try (Scope scope = span.makeCurrent()) { // Obtain the trace ID. System.out.println(span.getSpanContext().getTraceId()); // Obtain the span ID. System.out.println(span.getSpanContext().getSpanId()); childSpan(); } finally { span.end(); } } public void childSpan() { // Obtain the tracer. Tracer tracer = OpenTelemetryUtil.getTracer(); // Create a span. Span span = tracer.spanBuilder("Child Span").startSpan(); try (Scope scope = span.makeCurrent()) { // Obtain the trace ID. System.out.println(span.getSpanContext().getTraceId()); // Obtain the span ID. System.out.println(span.getSpanContext().getSpanId()); } finally { span.end(); } }
/** Visit the following link to view the complete code: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ // Create a nested span. fun parentSpan() { // Obtain the tracer. val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // Create a span. val span = tracer.spanBuilder("Parent Span").startSpan() try { span.makeCurrent().use { scope -> // Obtain the trace ID. println(span.spanContext.traceId) // Obtain the span ID. println(span.spanContext.spanId) childSpan() } } finally { span.end() } } // Create a nested span. fun childSpan() { // Obtain the tracer. val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // Create a span. val span = tracer.spanBuilder("Child Span").startSpan() try { span.makeCurrent().use { scope -> // Obtain the trace ID. println(span.spanContext.traceId) // Obtain the span ID. println(span.spanContext.spanId) } } finally { span.end() } }
Step 4: Run the project to view the reported trace data
Run the project and simulate a button tap on the application page.
You can view the logs in Logcat, including the span information that is exported by using LoggingSpanExporter.
Log on to the ARMS console. In the left-side navigation pane, choose . On the Application List 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.
Step 5: Connect the client application and server application
Change the format to pass trace data in the HTTP request header.
Different protocols use different HTTP request headers to pass the trace context. For example, OpenTelemtry uses the W3C Trace Context format by default and supports other formats, and Zipkin uses the B3 or B3 Multi format. For more information, see Specify the format to pass trace data.
Set the format to pass trace data for the client application based on the protocol used by the server application. This way, the Android client application and server application can be connected.
If the server application uses the W3C Trace Context format of OpenTelemetry, you do not need to specify the textPropagators parameter for the client application.
If the server application uses the B3 or B3 Multi format of Zipkin, set the textPropagators parameter to B3Propagator for the client application.
// Specify the B3 format to pass trace data. OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( B3Propagator.injectingMultiHeaders(), B3Propagator.injectingSingleHeader()))) .buildAndRegisterGlobal();
If the server application uses the Jaeger protocol, you must set the textPropagators parameter to JaegerPropagator for the client application.
// Specify the Jaeger format to pass trace data. OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( JaegerPropagator.getInstance()))) .buildAndRegisterGlobal();
You can also specify multiple formats to pass trace data.
// Specify the W3C Trace Context, B3, and Jaeger formats to pass trace data. // Specify the Jaeger format to pass trace data. 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.0 is an automatic instrumentation plug-in provided by OpenTelemetry for OkHttp frameworks. It automatically intercepts all network requests that are sent by using the OkHttp3 framework and creates traces.
Add the following dependencies to the build.gradle file:
dependencies { ... implementation 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0:2.3.0-alpha' implementation 'com.squareup.okhttp3:okhttp:4.12.0' }
Create the 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 access the server 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()) { // Use OkHttp to execute the network request. 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()) { // Process the response. String responseBody = response.body().string(); System.out.println(responseBody); } catch (IOException e) { // Handle errors. e.printStackTrace(); } } finally { span.end(); } }
View the trace between the client application and server application on the Trace Explorer page.
The following figure shows an example. In this example,
AsyncRequestZipkinServer
is an Android application andzipkin-demo-server
is a server application.