通过OpenTelemetry上报Android应用数据

通过OpenTelemetry为应用埋点并上报链路数据至可观测链路 OpenTelemetry 版后,可观测链路 OpenTelemetry 版即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTelemetry为Android应用埋点并上报数据。

前提条件

获取接入点信息

新版控制台

  1. 登录可观测链路 OpenTelemetry 版控制台,在左侧导航栏单击接入中心

  2. 开源框架区域单击OpenTelemetry卡片。

  3. 在弹出的OpenTelemetry面板中选择数据需要上报的地域。

    说明

    初次接入的地域将会自动进行资源初始化。

  4. 选择连接方式上报方式,然后复制接入点信息。

    • 连接方式:若您的服务部署在阿里云上,且所属地域与选择的接入地域一致,推荐使用阿里云内网方式,否则选择公网方式。

    • 上报方式:根据客户端支持的协议类型选择HTTP或gRPC协议上报数据。

    image.png

旧版控制台

  1. 登录可观测链路 OpenTelemetry 版控制台

  2. 在左侧导航栏单击集群配置,然后在右侧页面单击接入点信息页签。

  3. 在页面顶部选择需要接入的地域,然后在集群信息区域打开显示Token开关。

  4. 客户端采集工具区域单击OpenTelemetry

    相关信息列中,获取接入点信息。ot旧版中.jpg

    说明

    如果应用部署于阿里云生产环境,则选择阿里云VPC网络接入点,否则选择公网接入点。

示例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. 可观测链路 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