通過OpenTelemetry為應用埋點並上報鏈路資料至Managed Service for OpenTelemetry後,Managed Service for OpenTelemetry即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控資料。本文介紹如何使用OpenTelemetry為Android應用埋點並上報資料。
前提條件
樣本Demo
本文將通過具體樣本介紹如何使用OpenTelemetry上報Android應用程式的鏈路資料,該方法同樣適用於Java語言和Kotlin語言應用。
範例程式碼倉庫地址:opentelemetry-android-demo
步驟一:建立並配置應用
建立應用。
在Android Studio中建立應用,選擇Basic Views Activity應用模板,然後單擊Next。
選擇Java語言或者Kotlin語言,SDK支援的最小版本選擇API 24: Android 7.0 (Nougat),然後單擊Finish。
添加依賴項。
在Module或Project層級的build.gradle中,添加以下依賴。
本例中使用的OpenTelemetry Java SDK版本為1.25.0,更多版本請參見Opentelemetry Java Releases。build.gradle完整範例程式碼請參見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'
添加網路設定。
在
app/res/xml
目錄下建立network_security_config.xml檔案並添加以下內容。<!-- 查看檔案完整內容: 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"> <!-- 請將下面的網域名稱替換為您在前提條件中擷取的存取點,請不要包含"http://"、連接埠號碼和URL路徑 --> <domain includeSubdomains="true">tracing-analysis-dc-hz.aliyuncs.com</domain> </domain-config> </network-security-config>
修改
app/src/main/AndroidManifest.xml
檔案,添加以下兩行內容為應用開啟網路許可權。<!-- 查看檔案完整內容: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/AndroidManifest.xml --> <?xml version="1.0" encoding="utf-8"?> <manifest ...> <!-- 添加下面一行內容,以開啟網路許可權 --> <uses-permission android:name="android.permission.INTERNET" /> <application ... <!-- 添加下面一行內容,對要上報資料的網域名稱進行網路設定 --> android:networkSecurityConfig="@xml/network_security_config" ...> ... </application> </manifest>
步驟二:OpenTelemetry初始化
建立OpenTelemetry工具類。
在MainActivity所在的同級目錄下建立OpenTelemetryUtil檔案並添加以下內容。
方法一:通過gRPC上報Trace資料
/** 請訪問以下連結擷取完整代碼 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( // 請將<your-service-name>替換為您的應用程式名稱。 ResourceAttributes.SERVICE_NAME, "<your-service-name>", // 請將<your-host-name>替換為您的主機名稱。 ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路資料列印到日誌/命令列,如不需要請注釋這一行。 // 請將<gRPC-endpoint>替換為從前提條件中擷取的存取點,<gRPC-token>替換為鑒權Token。 .addSpanProcessor(BatchSpanProcessor.builder( OtlpGrpcSpanExporter.builder() .setEndpoint("<gRPC-endpoint>") // 例如:http://tracing-analysis-dc-hz.aliyuncs.com:8090 .addHeader("Authentication", "<gRPC-token>") // 例如:xxxx@xxxx_xxxx@xxxx .build()).build() ) .setResource(otelResource) .build(); OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); // 擷取tracer,用來建立Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0");
/** 請訪問以下連結擷取完整代碼 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>", // 請將<your-service-name>替換為您的應用程式名稱。 ResourceAttributes.HOST_NAME, "<your-host-name>" // 請將<your-host-name>替換為您的主機名稱。 ) ) ) /* 使用gRPC協議上報鏈路資料 */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路資料列印到日誌/命令列,如不需要請注釋這一行。 // 請將<gRPC-endpoint>替換為從前提條件中擷取的存取點,<gRPC-token>替換為鑒權Token。 .addSpanProcessor( BatchSpanProcessor.builder( OtlpGrpcSpanExporter.builder() .setEndpoint("<gRPC-endpoint>") // 例如:http://tracing-analysis-dc-hz.aliyuncs.com:8090 .addHeader("Authentication", "<gRPC-token>") // 例如:xxxx@xxxx_xxxx@xxxx .build() ).build() ) .setResource(otelResource) .build() val openTelemetry: OpenTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal() // 擷取tracer,用來建立Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
方法二:通過HTTP上報Trace資料
/** 請訪問以下連結擷取完整代碼 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( // 請將<your-service-name>替換為您的應用程式名稱。 ResourceAttributes.SERVICE_NAME, "<your-service-name>", // 請將<your-host-name>替換為您的主機名稱。 ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路資料列印到日誌/命令列,如不需要請注釋這一行 // 請將<HTTP-endpoint>替換為從前提條件中擷取的存取點。 .addSpanProcessor(BatchSpanProcessor.builder( OtlpHttpSpanExporter.builder() .setEndpoint("<HTTP-endpoint>") // 例如 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(); // 擷取tracer,用來建立Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0");
/** 請訪問以下連結擷取完整代碼 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>", // 請將<your-service-name>替換為您的應用程式名稱。 ResourceAttributes.HOST_NAME, "<your-host-name>" // 請將<your-host-name>替換為您的主機名稱。 ) ) ) /* 使用HTTP協議上報鏈路資料 */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路資料列印到日誌/命令列,如不需要請注釋這一行。 // 請將<HTTP-endpoint>替換為從前提條件中擷取的存取點。 .addSpanProcessor(BatchSpanProcessor.builder( OtlpHttpSpanExporter.builder() .setEndpoint("<HTTP-endpoint>") // 例如: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() // 擷取tracer,用來建立Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
在應用程式初始化時對OpenTelemetry進行初始化。
在MainActivity的onCreate方法中調用OpenTelemetryUtil.init()方法。
/** 請訪問以下連結擷取完整代碼 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); // 請添加這一行,對OpenTelemetry進行初始化。 OpenTelemetryUtil.init(); ... } ... }
/** 請訪問以下連結擷取完整代碼 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) // 請添加這一行,OpenTelemetry初始化。 OpenTelemetryUtil.init() ... } }
步驟三:建立Span追蹤鏈路資料
建立Span。
在FirstFragment檔案的按鈕點擊事件監聽方法中,建立一個名為First Fragment Button onClick的Span。
/** 請訪問以下連結擷取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ public void onClick(View view) { // 擷取Tracer Tracer tracer = OpenTelemetryUtil.getTracer(); // 建立Span Span span = tracer.spanBuilder("First Fragment Button onClick").startSpan(); try (Scope scope = span.makeCurrent()) { // 擷取traceId System.out.println(span.getSpanContext().getTraceId()); ... } finally { span.end(); } }
/** 請訪問以下連結擷取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ binding.buttonFirst.setOnClickListener { // 擷取Tracer val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // 建立Span val span = tracer.spanBuilder("First Fragment Button onClick").startSpan() try { span.makeCurrent().use { scope -> // 擷取traceId println(span.spanContext.traceId) // 擷取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() } }
為Span設定屬性和事件(Event)。
/** 請訪問以下連結擷取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ // 設定屬性 span.setAttribute("key", "value"); Attributes eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L); // 添加事件 span.addEvent("onClick", eventAttributes);
/** 請訪問以下連結擷取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ // 設定屬性 span.setAttribute("key", "value") val eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L ) // 添加事件 span.addEvent("onClick", eventAttributes)
為Span設定狀態(Status)。
/** 請訪問以下連結擷取完整代碼 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) { // 設定Span狀態 span.setStatus(StatusCode.ERROR, "Something wrong in onClick"); throw t; } finally { span.end(); }
/** 請訪問以下連結擷取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ ... try { ... } catch (t: Throwable) { // 為Span設定狀態 span.setStatus(StatusCode.ERROR, "Something wrong in onClick") throw t } finally { span.end() }
建立嵌套的Span。
/** 請訪問以下連結擷取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ public void parentSpan() { // 擷取Tracer Tracer tracer = OpenTelemetryUtil.getTracer(); // 建立Span Span span = tracer.spanBuilder("Parent Span").startSpan(); try (Scope scope = span.makeCurrent()) { // 擷取traceId System.out.println(span.getSpanContext().getTraceId()); // 擷取spanId System.out.println(span.getSpanContext().getSpanId()); childSpan(); } finally { span.end(); } } public void childSpan() { // 擷取Tracer Tracer tracer = OpenTelemetryUtil.getTracer(); // 建立Span Span span = tracer.spanBuilder("Child Span").startSpan(); try (Scope scope = span.makeCurrent()) { // 擷取traceId System.out.println(span.getSpanContext().getTraceId()); // 擷取spanId System.out.println(span.getSpanContext().getSpanId()); } finally { span.end(); } }
/** 請訪問以下連結擷取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ // 嵌套Span fun parentSpan() { // 擷取Tracer val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // 建立Span val span = tracer.spanBuilder("Parent Span").startSpan() try { span.makeCurrent().use { scope -> // 擷取traceId println(span.spanContext.traceId) // 擷取spanId println(span.spanContext.spanId) childSpan() } } finally { span.end() } } // 嵌套Span fun childSpan() { // 擷取Tracer val tracer: Tracer = OpenTelemetryUtil.getTracer()!! // 建立Span val span = tracer.spanBuilder("Child Span").startSpan() try { span.makeCurrent().use { scope -> // 擷取traceId println(span.spanContext.traceId) // 擷取spanId println(span.spanContext.spanId) } } finally { span.end() } }
步驟四:運行專案查看上報的鏈路資料
運行專案,單擊Demo應用頁面中的按鈕。
通過logcat查看日誌,可以看到通過LoggingSpanExporter匯出的Span資訊。
登入ARMS控制台後,在 頁面選擇目標應用,查看鏈路資料。
說明語言列顯示表徵圖的應用為接入應用監控的應用,顯示-表徵圖的應用為接入可觀測鏈路 OpenTelemetry 版的應用。
步驟五:打通用戶端與服務端應用鏈路
修改Header中的Trace透傳格式。
不同協議使用不同的HTTP Header向下遊傳遞Trace上下文,如OpenTelemtry預設使用W3C Trace Context格式(也支援修改為其他格式),而Zipkin使用B3或B3 Multi格式。關於透傳格式的更多資訊,請參見OpenTelemetry指定透傳Header格式。
根據服務端使用的協議類型,在用戶端中設定對應的透傳格式(textPropagators),以實現Android用戶端應用與服務端應用鏈路打通:
如果服務端使用OpenTelemetry預設的W3C Trace Context格式,用戶端無需設定textPropagators。
如果服務端使用Zipkin的B3/B3 Multi格式,用戶端的textPropagators需要設定為B3Propagator。
// 設定B3透傳格式。 OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( B3Propagator.injectingMultiHeaders(), B3Propagator.injectingSingleHeader()))) .buildAndRegisterGlobal();
如果服務端使用Jaeger協議,用戶端的textPropagators需設定為JaegerPropagator。
// 設定Jaeger透傳格式。 OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( JaegerPropagator.getInstance()))) .buildAndRegisterGlobal();
也可以同時設定多種Trace透傳格式。
// 同時使用W3C Trace Context、B3、Jaeger三種Trace透傳格式。 // 設定Jaeger透傳格式。 OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( W3CTraceContextPropagator.getInstance(), B3Propagator.injectingMultiHeaders(), B3Propagator.injectingSingleHeader() JaegerPropagator.getInstance()))) .buildAndRegisterGlobal();
匯入okhttp3和opentelemetry-okhttp。
opentelemetry-okhttp-3.0是OpenTelemetry提供的針對OkHttp的自動埋點外掛程式,可以自動攔截所有通過OkHttp3發出的網路請求並建立調用鏈。
在build.gradle中添加以下兩個依賴。
dependencies { ... implementation 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0:2.3.0-alpha' implementation 'com.squareup.okhttp3:okhttp:4.12.0' }
建立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(); } }
使用OkHttp3進行網路請求,訪問服務端。
private void callHttpService() throws IOException { Tracer tracer = OpenTelemetryUtil.getTracer(); // 建立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()) { // 執行網路請求,例如使用 OkHttp OkHttpConfiguration configuration = new OkHttpConfiguration(); Call.Factory tracedClient = configuration.createTracedClient(GlobalOpenTelemetry.get()); Request request = new Request.Builder().url("${服務端地址}").get().build(); Call call = tracedClient.newCall(request); try (Response response = call.execute()) { // 處理響應 String responseBody = response.body().string(); System.out.println(responseBody); } catch (IOException e) { // 處理錯誤 e.printStackTrace(); } } finally { span.end(); } }
在調用鏈分析頁面查看用戶端與服務端打通的調用鏈。
如下圖所示,
AsyncRequestZipkinServer
為Android應用,zipkin-demo-server
為服務端應用。