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
To obtain an endpoint of OpenTelemetry, perform the following steps:
In the left-side navigation pane, click Cluster Configurations. On the page that appears, click the Access point information tab.
In the top navigation bar, select a region. In the Cluster Information section, turn on Show Token.
Set the Client parameter to OpenTelemetry.
Obtain an endpoint of OpenTelemetry in the Related Information column of the table in the lower part.
Note
If your application is deployed in an Alibaba Cloud production environment, use a Virtual Private Cloud (VPC) endpoint. Otherwise, use a public endpoint.
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.
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.
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-configcleartextTrafficPermitted="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. --><domainincludeSubdomains="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-permissionandroid:name="android.permission.INTERNET" /><application...
<!--Addthefollowinglinetoconfigurethenetworkforthedomainnametowhichyouwanttoreportdata.-->
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
Java
Kotlin
/**
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
*/ResourceotelResource= 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>"
)
)
);
SdkTracerProvidersdkTracerProvider= 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();
OpenTelemetryopenTelemetry= 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
Java
Kotlin
/**
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
*/ResourceotelResource= 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>"
)
)
);
SdkTracerProvidersdkTracerProvider= 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();
OpenTelemetryopenTelemetry= 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.
Java
Kotlin
/**
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
*/
...
publicclassMainActivityextendsAppCompatActivity {
...
@OverrideprotectedvoidonCreate(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
*/
...
classMainActivity : AppCompatActivity() {
...
overridefunonCreate(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.
Java
Kotlin
/**
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
*/publicvoidonClick(View view) {
// Obtain the tracer.Tracertracer= OpenTelemetryUtil.getTracer();
// Create a span.Spanspan= tracer.spanBuilder("First Fragment Button onClick").startSpan();
try (Scopescope= 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.
Java
Kotlin
/**
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");
AttributeseventAttributes= 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.
Java
Kotlin
/**
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 (Scopescope= 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.
Java
Kotlin
/**
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
*/publicvoidparentSpan() {
// Obtain the tracer.Tracertracer= OpenTelemetryUtil.getTracer();
// Create a span.Spanspan= tracer.spanBuilder("Parent Span").startSpan();
try (Scopescope= 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();
}
}
publicvoidchildSpan() {
// Obtain the tracer.Tracertracer= OpenTelemetryUtil.getTracer();
// Create a span.Spanspan= tracer.spanBuilder("Child Span").startSpan();
try (Scopescope= 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.funparentSpan() {
// 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.funchildSpan() {
// 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 Application Monitoring > Application List. On the Application List page, click the name of the application. On the page that appears, view the trace data.
Note
If 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. OpenTelemetryopenTelemetry= 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. OpenTelemetryopenTelemetry= 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. OpenTelemetryopenTelemetry= 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:
View the complete code for asynchronously calling the callHttpService method in the onClick event
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;
import com.example.androidjavademo.databinding.FragmentFirstBinding;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
publicclassFirstFragmentextendsFragment {
private FragmentFirstBinding binding;
@Overridepublic View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
binding = FragmentFirstBinding.inflate(inflater, container, false);
return binding.getRoot();
}
publicvoidonViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.buttonFirst.setOnClickListener(newView.OnClickListener() {
@OverridepublicvoidonClick(View view) {
// Obtain the tracer.Tracertracer= OpenTelemetryUtil.getTracer();
// Create a span.Spanspan= tracer.spanBuilder("First Fragment Button onClick").startSpan();
try (Scopescope= span.makeCurrent()) {
// Obtain the trace ID.
System.out.println(span.getSpanContext().getTraceId());
// Obtain the span ID.
System.out.println(span.getSpanContext().getSpanId());
// Configure attributes.
span.setAttribute("key", "value");
AttributeseventAttributes= Attributes.of(
AttributeKey.stringKey("key"), "value",
AttributeKey.longKey("result"), 0L);
// Add an event.
span.addEvent("onClick", eventAttributes);
parentSpan();
NavHostFragment.findNavController(FirstFragment.this)
.navigate(R.id.action_FirstFragment_to_SecondFragment);
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR, "Something wrong in onClick");
throw t;
} finally {
span.end();
}
}
});
}
@OverridepublicvoidonDestroyView() {
super.onDestroyView();
binding = null;
}
publicvoidparentSpan() {
ExecutorServiceexecutorService= Executors.newSingleThreadExecutor();
Handlerhandler=newHandler(Looper.getMainLooper());
// Obtain the tracer.Tracertracer= OpenTelemetryUtil.getTracer();
// Create a span.Spanspan= tracer.spanBuilder("Parent Span").startSpan();
try (Scopescope= span.makeCurrent()) {
// Obtain the trace ID.
System.out.println(span.getSpanContext().getTraceId());
// Obtain the span ID.
System.out.println(span.getSpanContext().getSpanId());
executorService.execute(newRunnable() {
@Overridepublicvoidrun() {
// Execute the network request.try {
callHttpService();
} catch (IOException e) {
thrownewRuntimeException(e);
}
// After the execution is complete, use the handler to switch back to the main thread to process the result.
handler.post(newRunnable() {
@Overridepublicvoidrun() {
// Update the user interface (UI) and perform other operations.
childSpan();
}
});
}
});
executorService.shutdown();
} finally {
span.end();
}
}
publicvoidchildSpan() {
// Obtain the tracer.Tracertracer= OpenTelemetryUtil.getTracer();
// Create a span.Spanspan= tracer.spanBuilder("Child Span").startSpan();
try (Scopescope= 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();
}
}
privatevoidcallHttpService()throws IOException {
Tracertracer= OpenTelemetryUtil.getTracer();
// Create a span.Spanspan= tracer.spanBuilder("AsyncRequestZipkinServer").startSpan();
System.out.println("AsyncRequestZipkinServer TraceID: " + span.getSpanContext().getTraceId());
System.out.println("AsyncRequestZipkinServer SpanID: " + span.getSpanContext().getSpanId());
try (Scopescope= span.makeCurrent()) {
// Use OkHttp to execute the network request.OkHttpConfigurationconfiguration=newOkHttpConfiguration();
Call.FactorytracedClient= configuration.createTracedClient(GlobalOpenTelemetry.get());
Requestrequest=newRequest.Builder().url("${Server address}").get().build();
Callcall= tracedClient.newCall(request);
try (Responseresponse= call.execute()) {
// Process the response.StringresponseBody= 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 and zipkin-demo-server is a server application.