通過OpenTelemetry上報(bào)Android應(yīng)用數(shù)據(jù)
通過OpenTelemetry為應(yīng)用埋點(diǎn)并上報(bào)鏈路數(shù)據(jù)至可觀測(cè)鏈路 OpenTelemetry 版后,可觀測(cè)鏈路 OpenTelemetry 版即可開始監(jiān)控應(yīng)用,您可以查看應(yīng)用拓?fù)洹⒄{(diào)用鏈路、異常事務(wù)、慢事務(wù)和SQL分析等一系列監(jiān)控?cái)?shù)據(jù)。本文介紹如何使用OpenTelemetry為Android應(yīng)用埋點(diǎn)并上報(bào)數(shù)據(jù)。
前提條件
新版控制臺(tái)
登錄可觀測(cè)鏈路 OpenTelemetry 版控制臺(tái),在左側(cè)導(dǎo)航欄單擊接入中心。
在開源框架區(qū)域單擊OpenTelemetry卡片。
在彈出的OpenTelemetry面板中選擇數(shù)據(jù)需要上報(bào)的地域。
說明初次接入的地域?qū)?huì)自動(dòng)進(jìn)行資源初始化。
選擇連接方式和上報(bào)方式,然后復(fù)制接入點(diǎn)信息。
連接方式:若您的服務(wù)部署在阿里云上,且所屬地域與選擇的接入地域一致,推薦使用阿里云內(nèi)網(wǎng)方式,否則選擇公網(wǎng)方式。
上報(bào)方式:根據(jù)客戶端支持的協(xié)議類型選擇HTTP或gRPC協(xié)議上報(bào)數(shù)據(jù)。
舊版控制臺(tái)
在左側(cè)導(dǎo)航欄單擊集群配置,然后在右側(cè)頁面單擊接入點(diǎn)信息頁簽。
在頁面頂部選擇需要接入的地域,然后在集群信息區(qū)域打開顯示Token開關(guān)。
在客戶端采集工具區(qū)域單擊OpenTelemetry。
在相關(guān)信息列中,獲取接入點(diǎn)信息。
說明如果應(yīng)用部署于阿里云生產(chǎn)環(huán)境,則選擇阿里云VPC網(wǎng)絡(luò)接入點(diǎn),否則選擇公網(wǎng)接入點(diǎn)。
示例Demo
本文將通過具體示例介紹如何使用OpenTelemetry上報(bào)Android應(yīng)用程序的鏈路數(shù)據(jù),該方法同樣適用于Java語言和Kotlin語言應(yīng)用。
示例代碼倉庫地址:opentelemetry-android-demo
步驟一:創(chuàng)建并配置應(yīng)用
創(chuàng)建應(yīng)用。
在Android Studio中新建應(yīng)用,選擇Basic Views Activity應(yīng)用模板,然后單擊Next。
選擇Java語言或者Kotlin語言,SDK支持的最小版本選擇API 24: Android 7.0 (Nougat),然后單擊Finish。
添加依賴項(xiàng)。
在Module或Project級(jí)別的build.gradle中,添加以下依賴。
本例中使用的OpenTelemetry Java SDK版本為1.25.0,更多版本請(qǐng)參見Opentelemetry Java Releases。build.gradle完整示例代碼請(qǐng)參見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'
添加網(wǎng)絡(luò)配置。
在
app/res/xml
目錄下創(chuàng)建network_security_config.xml文件并添加以下內(nèi)容。<!-- 查看文件完整內(nèi)容: 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"> <!-- 請(qǐng)將下面的域名替換為您在前提條件中獲取的接入點(diǎn),請(qǐng)不要包含"http://"、端口號(hào)和URL路徑 --> <domain includeSubdomains="true">tracing-analysis-dc-hz.aliyuncs.com</domain> </domain-config> </network-security-config>
修改
app/src/main/AndroidManifest.xml
文件,添加以下兩行內(nèi)容為應(yīng)用開啟網(wǎng)絡(luò)權(quán)限。<!-- 查看文件完整內(nèi)容: https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/AndroidManifest.xml --> <?xml version="1.0" encoding="utf-8"?> <manifest ...> <!-- 添加下面一行內(nèi)容,以開啟網(wǎng)絡(luò)權(quán)限 --> <uses-permission android:name="android.permission.INTERNET" /> <application ... <!-- 添加下面一行內(nèi)容,對(duì)要上報(bào)數(shù)據(jù)的域名進(jìn)行網(wǎng)絡(luò)配置 --> android:networkSecurityConfig="@xml/network_security_config" ...> ... </application> </manifest>
步驟二:OpenTelemetry初始化
創(chuàng)建OpenTelemetry工具類。
在MainActivity所在的同級(jí)目錄下創(chuàng)建OpenTelemetryUtil文件并添加以下內(nèi)容。
方法一:通過gRPC上報(bào)Trace數(shù)據(jù)
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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( // 請(qǐng)將<your-service-name>替換為您的應(yīng)用名。 ResourceAttributes.SERVICE_NAME, "<your-service-name>", // 請(qǐng)將<your-host-name>替換為您的主機(jī)名。 ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路數(shù)據(jù)打印到日志/命令行,如不需要請(qǐng)注釋這一行。 // 請(qǐng)將<gRPC-endpoint>替換為從前提條件中獲取的接入點(diǎn),<gRPC-token>替換為鑒權(quán)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,用來創(chuàng)建Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0");
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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>", // 請(qǐng)將<your-service-name>替換為您的應(yīng)用名。 ResourceAttributes.HOST_NAME, "<your-host-name>" // 請(qǐng)將<your-host-name>替換為您的主機(jī)名。 ) ) ) /* 使用gRPC協(xié)議上報(bào)鏈路數(shù)據(jù) */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路數(shù)據(jù)打印到日志/命令行,如不需要請(qǐng)注釋這一行。 // 請(qǐng)將<gRPC-endpoint>替換為從前提條件中獲取的接入點(diǎn),<gRPC-token>替換為鑒權(quán)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,用來創(chuàng)建Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
方法二:通過HTTP上報(bào)Trace數(shù)據(jù)
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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( // 請(qǐng)將<your-service-name>替換為您的應(yīng)用名。 ResourceAttributes.SERVICE_NAME, "<your-service-name>", // 請(qǐng)將<your-host-name>替換為您的主機(jī)名。 ResourceAttributes.HOST_NAME, "<your-host-name>" ) ) ); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路數(shù)據(jù)打印到日志/命令行,如不需要請(qǐng)注釋這一行 // 請(qǐng)將<HTTP-endpoint>替換為從前提條件中獲取的接入點(diǎn)。 .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,用來創(chuàng)建Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0");
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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>", // 請(qǐng)將<your-service-name>替換為您的應(yīng)用名。 ResourceAttributes.HOST_NAME, "<your-host-name>" // 請(qǐng)將<your-host-name>替換為您的主機(jī)名。 ) ) ) /* 使用HTTP協(xié)議上報(bào)鏈路數(shù)據(jù) */ val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create())) // 可選,將鏈路數(shù)據(jù)打印到日志/命令行,如不需要請(qǐng)注釋這一行。 // 請(qǐng)將<HTTP-endpoint>替換為從前提條件中獲取的接入點(diǎn)。 .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,用來創(chuàng)建Span。 tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
在應(yīng)用程序初始化時(shí)對(duì)OpenTelemetry進(jìn)行初始化。
在MainActivity的onCreate方法中調(diào)用OpenTelemetryUtil.init()方法。
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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); // 請(qǐng)?zhí)砑舆@一行,對(duì)OpenTelemetry進(jìn)行初始化。 OpenTelemetryUtil.init(); ... } ... }
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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) // 請(qǐng)?zhí)砑舆@一行,OpenTelemetry初始化。 OpenTelemetryUtil.init() ... } }
步驟三:創(chuàng)建Span追蹤鏈路數(shù)據(jù)
創(chuàng)建Span。
在FirstFragment文件的按鈕點(diǎn)擊事件監(jiān)聽方法中,創(chuàng)建一個(gè)名為First Fragment Button onClick的Span。
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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(); // 創(chuàng)建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(); } }
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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()!! // 創(chuàng)建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設(shè)置屬性和事件(Event)。
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidJavaDemo/app/src/main/java/com/example/androidjavademo/FirstFragment.java */ // 設(shè)置屬性 span.setAttribute("key", "value"); Attributes eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L); // 添加事件 span.addEvent("onClick", eventAttributes);
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ // 設(shè)置屬性 span.setAttribute("key", "value") val eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L ) // 添加事件 span.addEvent("onClick", eventAttributes)
為Span設(shè)置狀態(tài)(Status)。
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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) { // 設(shè)置Span狀態(tài) span.setStatus(StatusCode.ERROR, "Something wrong in onClick"); throw t; } finally { span.end(); }
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 https://github.com/alibabacloud-observability/android-demo/blob/master/AndroidKotlinDemo/app/src/main/java/com/example/androidkotlindemo/FirstFragment.kt */ ... try { ... } catch (t: Throwable) { // 為Span設(shè)置狀態(tài) span.setStatus(StatusCode.ERROR, "Something wrong in onClick") throw t } finally { span.end() }
創(chuàng)建嵌套的Span。
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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(); // 創(chuàng)建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(); // 創(chuàng)建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(); } }
/** 請(qǐng)?jiān)L問以下鏈接獲取完整代碼 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()!! // 創(chuàng)建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()!! // 創(chuàng)建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() } }
步驟四:運(yùn)行項(xiàng)目查看上報(bào)的鏈路數(shù)據(jù)
運(yùn)行項(xiàng)目,單擊Demo應(yīng)用頁面中的按鈕。
通過logcat查看日志,可以看到通過LoggingSpanExporter導(dǎo)出的Span信息。
在可觀測(cè)鏈路 OpenTelemetry 版控制臺(tái)的應(yīng)用列表頁面選擇目標(biāo)應(yīng)用,查看鏈路數(shù)據(jù)。
步驟五:打通客戶端與服務(wù)端應(yīng)用鏈路
修改Header中的Trace透?jìng)鞲袷健?/p>
不同協(xié)議使用不同的HTTP Header向下游傳遞Trace上下文,如OpenTelemtry默認(rèn)使用W3C Trace Context格式(也支持修改為其他格式),而Zipkin使用B3或B3 Multi格式。關(guān)于透?jìng)鞲袷降母嘈畔ⅲ?qǐng)參見OpenTelemetry指定透?jìng)鱄eader格式。
根據(jù)服務(wù)端使用的協(xié)議類型,在客戶端中設(shè)置對(duì)應(yīng)的透?jìng)鞲袷剑╰extPropagators),以實(shí)現(xiàn)Android客戶端應(yīng)用與服務(wù)端應(yīng)用鏈路打通:
如果服務(wù)端使用OpenTelemetry默認(rèn)的W3C Trace Context格式,客戶端無需設(shè)置textPropagators。
如果服務(wù)端使用Zipkin的B3/B3 Multi格式,客戶端的textPropagators需要設(shè)置為B3Propagator。
// 設(shè)置B3透?jìng)鞲袷健?OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( B3Propagator.injectingMultiHeaders(), B3Propagator.injectingSingleHeader()))) .buildAndRegisterGlobal();
如果服務(wù)端使用Jaeger協(xié)議,客戶端的textPropagators需設(shè)置為JaegerPropagator。
// 設(shè)置Jaeger透?jìng)鞲袷健?OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( JaegerPropagator.getInstance()))) .buildAndRegisterGlobal();
也可以同時(shí)設(shè)置多種Trace透?jìng)鞲袷健?/p>
// 同時(shí)使用W3C Trace Context、B3、Jaeger三種Trace透?jìng)鞲袷健?// 設(shè)置Jaeger透?jìng)鞲袷健?OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(TextMapPropagator.composite( W3CTraceContextPropagator.getInstance(), B3Propagator.injectingMultiHeaders(), B3Propagator.injectingSingleHeader() JaegerPropagator.getInstance()))) .buildAndRegisterGlobal();
導(dǎo)入okhttp3和opentelemetry-okhttp。
opentelemetry-okhttp-3.0是OpenTelemetry提供的針對(duì)OkHttp的自動(dòng)埋點(diǎn)插件,可以自動(dòng)攔截所有通過OkHttp3發(fā)出的網(wǎng)絡(luò)請(qǐng)求并創(chuàng)建調(diào)用鏈。
在build.gradle中添加以下兩個(gè)依賴。
dependencies { ... implementation 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0:2.3.0-alpha' implementation 'com.squareup.okhttp3:okhttp:4.12.0' }
創(chuàng)建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進(jìn)行網(wǎng)絡(luò)請(qǐng)求,訪問服務(wù)端。
private void callHttpService() throws IOException { Tracer tracer = OpenTelemetryUtil.getTracer(); // 創(chuàng)建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()) { // 執(zhí)行網(wǎng)絡(luò)請(qǐng)求,例如使用 OkHttp OkHttpConfiguration configuration = new OkHttpConfiguration(); Call.Factory tracedClient = configuration.createTracedClient(GlobalOpenTelemetry.get()); Request request = new Request.Builder().url("${服務(wù)端地址}").get().build(); Call call = tracedClient.newCall(request); try (Response response = call.execute()) { // 處理響應(yīng) String responseBody = response.body().string(); System.out.println(responseBody); } catch (IOException e) { // 處理錯(cuò)誤 e.printStackTrace(); } } finally { span.end(); } }
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; public class FirstFragment extends Fragment { private FragmentFirstBinding binding; @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { binding = FragmentFirstBinding.inflate(inflater, container, false); return binding.getRoot(); } public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); binding.buttonFirst.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // 獲取Tracer Tracer tracer = OpenTelemetryUtil.getTracer(); // 創(chuàng)建Span Span span = tracer.spanBuilder("First Fragment Button onClick").startSpan(); try (Scope scope = span.makeCurrent()) { // 獲取traceId System.out.println(span.getSpanContext().getTraceId()); // 獲取spanId System.out.println(span.getSpanContext().getSpanId()); // 設(shè)置屬性 span.setAttribute("key", "value"); Attributes eventAttributes = Attributes.of( AttributeKey.stringKey("key"), "value", AttributeKey.longKey("result"), 0L); // 添加事件 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(); } } }); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } public void parentSpan() { ExecutorService executorService = Executors.newSingleThreadExecutor(); Handler handler = new Handler(Looper.getMainLooper()); // 獲取Tracer Tracer tracer = OpenTelemetryUtil.getTracer(); // 創(chuàng)建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()); executorService.execute(new Runnable() { @Override public void run() { // 執(zhí)行網(wǎng)絡(luò)請(qǐng)求 try { callHttpService(); } catch (IOException e) { throw new RuntimeException(e); } // 執(zhí)行完畢,通過handler切回主線程處理結(jié)果 handler.post(new Runnable() { @Override public void run() { // 處理UI更新或其他工作 childSpan(); } }); } }); executorService.shutdown(); } finally { span.end(); } } public void childSpan() { // 獲取Tracer Tracer tracer = OpenTelemetryUtil.getTracer(); // 創(chuàng)建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(); } } private void callHttpService() throws IOException { Tracer tracer = OpenTelemetryUtil.getTracer(); // 創(chuàng)建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()) { // 執(zhí)行網(wǎng)絡(luò)請(qǐng)求,例如使用OkHttp OkHttpConfiguration configuration = new OkHttpConfiguration(); Call.Factory tracedClient = configuration.createTracedClient(GlobalOpenTelemetry.get()); Request request = new Request.Builder().url("${服務(wù)端地址}").get().build(); Call call = tracedClient.newCall(request); try (Response response = call.execute()) { // 處理響應(yīng) String responseBody = response.body().string(); System.out.println(responseBody); } catch (IOException e) { // 處理錯(cuò)誤 e.printStackTrace(); } } finally { span.end(); } } }
在調(diào)用鏈分析頁面查看客戶端與服務(wù)端打通的調(diào)用鏈。
如下圖所示,
AsyncRequestZipkinServer
為Android應(yīng)用,zipkin-demo-server
為服務(wù)端應(yīng)用。