全部產品
Search
文件中心

Application Real-Time Monitoring Service:通過OpenTelemetry上報Android應用資料

更新時間:Aug 08, 2024

通過OpenTelemetry為應用埋點並上報鏈路資料至Managed Service for OpenTelemetry後,Managed Service for OpenTelemetry即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控資料。本文介紹如何使用OpenTelemetry為Android應用埋點並上報資料。

前提條件

擷取存取點資訊

  1. 登入ARMS控制台,在左側導覽列單擊接入中心

  2. 服務端應用地區單擊OpenTelemetry卡片。

  3. 在彈出的OpenTelemetry面板中選擇資料需要上報的地區。

    說明

    初次接入的地區將會自動進行資源初始化。

  4. 選擇串連方式上報方式,然後複製存取點資訊。

    • 串連方式:若您的服務部署在阿里雲上,且所屬地區與選擇的接入地區一致,推薦使用阿里雲內網方式,否則選擇公網方式。

    • 上報方式:根據用戶端支援的協議類型選擇HTTP或gRPC協議上報資料。

    image.png

樣本Demo

本文將通過具體樣本介紹如何使用OpenTelemetry上報Android應用程式的鏈路資料,該方法同樣適用於Java語言和Kotlin語言應用。

範例程式碼倉庫地址:opentelemetry-android-demo

步驟一:建立並配置應用

  1. 建立應用。

    1. 在Android Studio中建立應用,選擇Basic Views Activity應用模板,然後單擊Nextimage..png

    2. 選擇Java語言或者Kotlin語言,SDK支援的最小版本選擇API 24: Android 7.0 (Nougat),然後單擊Finish

  2. 添加依賴項。

    在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'
  3. 添加網路設定。

    1. 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>
    2. 修改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初始化

  1. 建立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")
  2. 在應用程式初始化時對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追蹤鏈路資料

  1. 建立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()
        }
    }
  2. 為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)
  3. 為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()
    }
  4. 建立嵌套的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()
        }
    }

步驟四:運行專案查看上報的鏈路資料

  1. 運行專案,單擊Demo應用頁面中的按鈕。image..png

    通過logcat查看日誌,可以看到通過LoggingSpanExporter匯出的Span資訊。

    image..png

  2. 登入ARMS控制台後,在應用監控 > 應用列表頁面選擇目標應用,查看鏈路資料。

    說明

    語言列顯示image表徵圖的應用為接入應用監控的應用,顯示-表徵圖的應用為接入可觀測鏈路 OpenTelemetry 版的應用。

    image..png

步驟五:打通用戶端與服務端應用鏈路

  1. 修改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();
  2. 匯入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();
          }
      }
  3. 使用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();
        }
    }

    展開查看在onClick等事件中非同步呼叫callHttpService方法的完整代碼

    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();
                    // 建立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());
                        // 設定屬性
                        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();
            // 建立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() {
                        // 執行網路請求
                        try {
                            callHttpService();
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        // 執行完畢,通過handler切回主線程處理結果
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                // 處理UI更新或其他工作
                                childSpan();
                            }
                        });
                    }
                });
    
                executorService.shutdown();
            } 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();
            }
        }
    
        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();
            }
        }
    
    }
  4. 調用鏈分析頁面查看用戶端與服務端打通的調用鏈。

    如下圖所示,AsyncRequestZipkinServer為Android應用,zipkin-demo-server為服務端應用。

    image