更新时间:2026-01-21 GMT+08:00
分享

通过OpenTelemetry Java Agent接入APM

华为云APM兼容OpenTelemetry协议,支持直接接收通过OpenTelemetry SDK或Agent上报的链路追踪数据。本文将介绍如何通过OpenTelemetry Java Agent实现无侵入式埋点,或通过SDK进行手动埋点,并将链路数据对接到APM。

示例demo

使用springboot实现一个简单掷骰子应用。

  1. 编写业务代码,初始化一个springboot项目。

    创建RollDice.java文件,内容如下:

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.Optional;
    import java.util.concurrent.ThreadLocalRandom;
    
    @RestController
    public class RollDice {
    
        @GetMapping("/rolldice")
        public String rollDiceAndSave(@RequestParam("player") Optional<String> player) {
            int result = 0;
            //掷骰子
            try {
                Thread.sleep(100);
                result = ThreadLocalRandom.current().nextInt(1, 7);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            saveResult(player.orElse("Anonymous player"), result);
    
            return Integer.toString(result);
        }
    
        private void saveResult(String player, int dicePoint) {
            //模拟数据库操作耗时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    application.properties 内容如下:

    server.port=8080

  2. 启动。

    java -jar otel-demo-0.0.1-SNAPSHOT.jar & 
    curl http://localhost:8080/rolldice?player=Alex

使用Java agent自动埋点

OpenTelemetry java agent 支持对一些常用库/框架进行无侵入埋点方式,详情参考自动埋点支持的库/框架

  1. 获取探针
  2. 添加探针启动参数并重启应用。添加启动命令并重启应用,命令格式如下:

    java -javaagent:探针安装路径  \
      -Dotel.exporter.otlp.protocol=grpc  \
      -Dotel.exporter.otlp.traces.endpoint=上报地址 \
      -Dotel.exporter.otlp.headers=Authentication=鉴权信息  \
      -Dotel.service.name=应用名称.组件名称.环境名称  \  
      -Dotel.metrics.exporter=none  \
      -Dotel.logs.exporter=none  \
      -jar <yourApp>.jar  &
    
    curl http://localhost:8080/rolldice?player=Alex

  3. 登录APM控制台
  4. 单击左侧,选择“管理与监管> 应用性能管理 APM”,进入APM服务页面。
  5. 在左侧导航栏选择“链路追踪 > 指标”。
  6. 在界面左侧树单击环境,单击“概览”,切换至概览页签,在概览页签可以查看该实例的应用监控数据。详细操作参见指标

使用Java SDK手动埋点

当自动埋点不满足业务场景,或者需要自定义业务埋点逻辑时,可以引入OpenTelemetry SDK 进行手动埋点。

  1. 添加Maven依赖。

    在pom.xml中引入OpenTelemetry相关依赖

    <dependencies>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-api</artifactId>
            </dependency>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-sdk-trace</artifactId>
            </dependency>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-exporter-otlp</artifactId>
            </dependency>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-sdk</artifactId>
            </dependency>
            <dependency>
                <groupId>io.opentelemetry</groupId>
                <artifactId>opentelemetry-semconv</artifactId>
                <version>1.30.0-alpha</version>
            </dependency>
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>io.opentelemetry</groupId>
                    <artifactId>opentelemetry-bom</artifactId>
                    <version>1.30.0</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

  2. 初始化 OpenTelemetry SDK。

    1. 编写一个工具类,用于配置和创建 OpenTelemetry实例。
      import io.opentelemetry.api.OpenTelemetry;
      import io.opentelemetry.api.common.Attributes;
      import io.opentelemetry.api.trace.Tracer;
      import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
      import io.opentelemetry.context.propagation.ContextPropagators;
      import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
      import io.opentelemetry.sdk.OpenTelemetrySdk;
      import io.opentelemetry.sdk.resources.Resource;
      import io.opentelemetry.sdk.trace.SdkTracerProvider;
      import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
      import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
      
      public class OpenTelemetrySupport {
      
          static {
              // 获取OpenTelemetry Tracer
              Resource resource = Resource.getDefault()
                  .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "应用名称.组件名称.环境名称", // 应用名称需在APM链路追踪创建一个opentelemetry的应用,组件名称和环境名称任意填写
                      ResourceAttributes.SERVICE_VERSION, "1.0.0", // 版本号。
                      ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "test", // 部署环境,任意填写字符串。
                      ResourceAttributes.HOST_NAME, "127.0.0.1" // 请将 ${host-name} 替换为您的主机名,也任意填写字符串。
                  )));
      
              SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
                  .addSpanProcessor(BatchSpanProcessor.builder(
                      OtlpGrpcSpanExporter.builder().setEndpoint("接入地址") // 接入地址
                          .addHeader("Authentication", "应用鉴权信息") // 应用鉴权信息 
                          .build()).build())
                  .setResource(resource)
                  .build();
      
              OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
                  .setTracerProvider(sdkTracerProvider)
                  .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
                  .buildAndRegisterGlobal();
      
              tracer = openTelemetry.getTracer("OpenTelemetry Tracer", "1.0.0");
          }
      
          private static Tracer tracer;
      
          public static Tracer getTracer() {
              return tracer;
          }
      }
    2. 创建Span。
      import io.opentelemetry.api.common.Attributes;
      import io.opentelemetry.api.trace.Span;
      import io.opentelemetry.api.trace.SpanKind;
      import io.opentelemetry.api.trace.StatusCode;
      import io.opentelemetry.context.Scope;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.bind.annotation.RestController;
      
      import java.util.Optional;
      import java.util.concurrent.ThreadLocalRandom;
      
      @RestController
      public class RollDice {
      
          @GetMapping("/rolldice")
          public String rollDiceAndSave(@RequestParam("player") Optional<String> player) {
              int result = 0;
              Span span = OpenTelemetrySupport.getTracer().spanBuilder("rolldice").setSpanKind(SpanKind.SERVER).startSpan();
              try (Scope scope = span.makeCurrent()) {
                  span.setStatus(StatusCode.OK);
                  span.setAttribute("dice.player", player.orElse("Anonymous player"));
                  span.addEvent("ev",
                      Attributes.builder().put("event1", "value1").build());  // 调用链的span详情页面上会显示后面的event1和value1
                  span.setAttribute("http.target", "rolldice");   // 作为server类型,必须设置该字段
                  span.setAttribute("http.method", "GET");  // 作为server类型,必须设置该字段
                  span.setAttribute("http.route", "{path}/**");  // 可选字段
                  span.setAttribute("http.status_code", 200);  // 可选字段
                  Attributes attributes = Attributes.builder()
                      .put("exception.type", "NullPoint")
                      .put("exception.message", "I am exception message")
                      .build();   // 可选字段,展示在调用链的span详情页面上
                  span.addEvent("exception", attributes);
                  //掷骰子
                  try {
                      Thread.sleep(100);
                      result = ThreadLocalRandom.current().nextInt(1, 7);
                  } catch (InterruptedException e) {
                      Thread.currentThread().interrupt();
                  }
                  saveResult(player.orElse("Anonymous player"), result);
              } catch (Throwable t) {
                  span.setStatus(StatusCode.ERROR, "handle parent span error");
              } finally {
                  span.end();
              }
              return Integer.toString(result);
          }
      
          private void saveResult(String player, int dicePoint) {
              Span span = OpenTelemetrySupport.getTracer()
                  .spanBuilder("saveResult")
                  .setSpanKind(SpanKind.CLIENT)
                  .startSpan();  // 数据库调用类型的span,必须设置为client类型,setParent这一步用于设置父span,标识父子span的关系,用以构造span的树桩结构
      
              try (Scope scope = span.makeCurrent()) {
                  span.setAttribute("db.system", "mysql"); // 数据库调用类型的span,必须设置为db.system类型,用来标识数据库的类型
                  span.setAttribute("db.statement",
                      "INSERT INTO dice_roll (player_name, dice_point) VALUES (" + player + ", " + dicePoint
                          + ")"); // 数据库调用类型的span,必须设置为db.statement,用来标识数据库的SQL语句
                  span.setAttribute("db.connection_string",
                      "127.0.0.1:3306:mysql"); // 数据库调用类型的span,必须设置为db.connection_string,用来标识数据库的连接对象
      
                  //模拟数据库操作耗时
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {
                      Thread.currentThread().interrupt();
                  }
              } catch (Throwable t) {
                  span.recordException(t);
                  span.setStatus(StatusCode.ERROR, "handle child span error");
              } finally {
                  span.end();
              }
          }
      }

  3. 登录APM控制台
  4. 单击左侧,选择“管理与监管> 应用性能管理 APM”,进入APM服务页面。
  5. 在左侧导航栏选择“链路追踪 > 指标”。
  6. 在界面左侧树单击环境,单击“概览”,切换至概览页签,在概览页签可以查看该实例的应用监控数据。详细操作参见指标

相关文档