在使用链路追踪控制台追踪应用的链路数据之前,需要将应用数据上报至链路追踪。本文介绍如何为应用埋点并上报链路数据。
前提条件
获取接入点信息
- 登录链路追踪Tracing Analysis控制台。
- 在左侧导航栏单击集群配置,然后在右侧页面单击接入点信息页签。
- 在页面顶部选择需要接入的地域,然后在集群信息区域打开显示Token开关。
- 在客户端采集工具区域单击OpenTelemetry。在下方表格的相关信息列中,获取接入点信息。说明
- 对于ACK集群应用,建议通过ARMS OpenTelemetry Collector上报应用数据。ARMS OpenTelemetry Collector支持本地集群内的链路采样与指标无损统计,在降低链路传输、存储成本的同时,不影响监控或告警指标的准确性。更多信息,请参见ARMS OpenTelemetry Collector
- 如果应用部署于阿里云生产环境,则选择私网接入点,否则选择公网接入点。
示例Demo
示例代码仓库地址:java-opentelemetry-demo
方法一:使用OpenTelemetry Java Agent自动埋点
OpenTelemetry Java Agent提供了无侵入的接入方式,支持数十种Java框架自动上传Trace数据,详细的Java框架列表,请参见Supported Libraries。
- 下载Java Agent。
- 通过修改Java启动的VM参数上报链路数据。
-javaagent:/path/to/opentelemetry-javaagent.jar //请将路径修改为您文件下载的实际地址。 -Dotel.resource.attributes=service.name=<appName> //<appName> 为应用名。 -Dotel.exporter.otlp.headers=Authentication=<token> -Dotel.exporter.otlp.endpoint=<endpoint>
- 如果您选择直接上报数据,请将
<token>
替换成从前提条件中获取的Token,将<endpoint>
替换成对应地域的Endpoint。例如:
-javaagent:/Users/carpela/Downloads/opentelemetry-javaagent.jar -Dotel.resource.attributes=service.name=ot-java-agent-sample -Dotel.exporter.otlp.headers=Authentication=b590xxxxuqs@3a75d95xxxxx9b_b59xxxxguqs@53dxxxx2afe8301 -Dotel.exporter.otlp.endpoint=http://tracing-analysis-dc-bj:8090
- 如果您选择使用OpenTelemetry Collector转发,则需删除
-Dotel.exporter.otlp.headers=Authentication=<token>
并修改<endpoint>
为您本地部署的服务地址。
- 如果您选择直接上报数据,请将
方法二:使用OpenTelemetry Java SDK手动埋点
OpenTelemetry Java SDK是OpenTelemetry Java Agent实现的基础,同时提供了丰富的自定义能力。当OpenTelemetry Java Agent的埋点不满足您的场景或者需要增加一些自定义业务埋点时,可以使用以下方式接入。
- 引入Maven POM依赖。
<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.23.0-alpha</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-bom</artifactId> <version>1.23.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
- 获取OpenTelemetry Tracer。
<logical-service-name>
为服务名,<host-name>
为主机名,请根据您的实际场景配置。- 如果您选择直接上报数据,请将以下代码中的
<token>
替换成前提条件中获取的Token,将<endpoint>
替换成对应地域的Endpoint。
package com.alibaba.arms.brightroar.console.util; 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, "<logical-service-name>", ResourceAttributes.HOST_NAME, "<host-name>" ))); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder() .setEndpoint("<endpoint>") .addHeader("Authentication", "<token>") .build()).build()) .setResource(resource) .build(); OpenTelemetry openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); tracer = openTelemetry.getTracer("<your_tracer_name>", "1.0.0"); } private static Tracer tracer; public static Tracer getTracer() { return tracer; } }
- 参考以下内容修改Controller代码和Service代码。
- Controller代码:
package com.alibaba.arms.brightroar.console.controller; import com.alibaba.arms.brightroar.console.service.UserService; import com.alibaba.arms.brightroar.console.util.OpenTelemetrySupport; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 参考文档: * 1. https://opentelemetry.io/docs/java/manual_instrumentation/ */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; private ExecutorService es = Executors.newFixedThreadPool(5); private void biz() { Tracer tracer = OpenTelemetrySupport.getTracer(); Span span = tracer.spanBuilder("biz (manual)") .setParent(Context.current().with(Span.current())) // 可选,自动设置 .startSpan(); try (Scope scope = span.makeCurrent()) { span.setAttribute("biz-id", "111"); es.submit(new Runnable() { @Override public void run() { Span asyncSpan = tracer.spanBuilder("async") .setParent(Context.current().with(span)) .startSpan(); try { Thread.sleep(1000L); // some async jobs } catch (Throwable e) { } asyncSpan.end(); } }); Thread.sleep(1000); // fake biz logic System.out.println("biz done"); OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); openTelemetry.getPropagators(); } catch (Throwable t) { span.setStatus(StatusCode.ERROR, "handle biz error"); } finally { span.end(); } } private void child(String userType) { Span span = OpenTelemetrySupport.getTracer().spanBuilder("child span").startSpan(); try (Scope scope = span.makeCurrent()) { span.setAttribute("user.type", userType); System.out.println(userType); biz(); } catch (Throwable t) { span.setStatus(StatusCode.ERROR, "handle child span error"); } finally { span.end(); } } @RequestMapping("/async") public String async() { System.out.println("UserController.async -- " + Thread.currentThread().getId()); Span span = OpenTelemetrySupport.getTracer().spanBuilder("parent span").startSpan(); span.setAttribute("user.id", "123456"); try (Scope scope = span.makeCurrent()) { userService.async(); child("vip"); } catch (Throwable t) { span.setStatus(StatusCode.ERROR, "handle parent span error"); } finally { span.end(); } return "async"; } }
- Service代码:
package com.alibaba.arms.brightroar.console.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class UserService { @Async public void async() { System.out.println("UserService.async -- " + Thread.currentThread().getId()); System.out.println("my name is async"); System.out.println("UserService.async -- "); } }
- Controller代码:
- 启动应用。
在链路追踪Tracing Analysis控制台的应用列表页面选择目标应用,查看链路数据。
方法三:同时使用Java Agent和Java SDK埋点
您可以在使用Java Agent获得自动埋点能力的同时,使用Java SDK添加自定义业务埋点。
- 下载Java Agent。
- 在方法二的Maven依赖基础上新增以下依赖。
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-extension-annotations</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId> <version>1.23.0-alpha</version> </dependency>
说明 其中opentelemetry-sdk-extension-autoconfigure
完成了SDK的自动配置,将Java Agent的配置传递到Java SDK中。展开查看完整的Maven POM依赖<dependencies> <dependency> <groupId>com.alibaba.arms</groupId> <artifactId>brightroar-dao</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <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-extension-annotations</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.23.0-alpha</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId> <version>1.23.0-alpha</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-bom</artifactId> <version>1.23.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
- 获取OpenTelemetry Tracer。同时使用Java Agent和Java SDK埋点时,无需再使用方法二中的OpenTelemetrySupport类获取Tracer。
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); Tracer tracer = openTelemetry.getTracer("instrumentation-library-name", "1.0.0");
- 参考以下内容修改Controller代码和Service代码。
- Controller代码如下,建议使用代码中的第一种和第二种方式。
package com.alibaba.arms.brightroar.console.controller; import com.alibaba.arms.brightroar.console.service.UserService; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import io.opentelemetry.extension.annotations.SpanAttribute; import io.opentelemetry.extension.annotations.WithSpan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 参考文档: * 1. https://opentelemetry.io/docs/java/manual_instrumentation/ */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; private ExecutorService es = Executors.newFixedThreadPool(5); // 第三种:获得 Tracer 纯手工埋点 private void biz() { Tracer tracer = GlobalOpenTelemetry.get().getTracer("tracer"); Span span = tracer.spanBuilder("biz (manual)") .setParent(Context.current().with(Span.current())) // 可选,自动设置 .startSpan(); try (Scope scope = span.makeCurrent()) { span.setAttribute("biz-id", "111"); es.submit(new Runnable() { @Override public void run() { Span asyncSpan = tracer.spanBuilder("async") .setParent(Context.current().with(span)) .startSpan(); try { Thread.sleep(1000L); // some async jobs } catch (Throwable e) { } asyncSpan.end(); } }); Thread.sleep(1000); // fake biz logic System.out.println("biz done"); OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); openTelemetry.getPropagators(); } catch (Throwable t) { span.setStatus(StatusCode.ERROR, "handle biz error"); } finally { span.end(); } } // 第二种:通过注解创建埋点 @WithSpan private void child(@SpanAttribute("user.type") String userType) { System.out.println(userType); biz(); } // 第一种:自动埋点,基于 API 手工添加信息 @RequestMapping("/async") public String async() { System.out.println("UserController.async -- " + Thread.currentThread().getId()); Span span = Span.current(); span.setAttribute("user.id", "123456"); userService.async(); child("vip"); return "async"; } }
- Service代码:
package com.alibaba.arms.brightroar.console.service; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class UserService { @Async public void async() { System.out.println("UserService.async -- " + Thread.currentThread().getId()); System.out.println("my name is async"); System.out.println("UserService.async -- "); } }
- Controller代码如下,建议使用代码中的第一种和第二种方式。
- 通过修改Java启动的VM参数上报链路数据。
-javaagent:/path/to/opentelemetry-javaagent.jar //请将路径修改为您文件下载的实际地址。 -Dotel.resource.attributes=service.name=<appName> //<appName> 为应用名。 -Dotel.exporter.otlp.headers=Authentication=<token> -Dotel.exporter.otlp.endpoint=<endpoint>
- 如果您选择直接上报数据,请将
<token>
替换成从前提条件中获取的Token,将<endpoint>
替换成对应地域的Endpoint。例如:
-javaagent:/Users/carpela/Downloads/opentelemetry-javaagent.jar -Dotel.resource.attributes=service.name=ot-java-agent-sample -Dotel.exporter.otlp.headers=Authentication=b590xxxxuqs@3a75d95xxxxx9b_b59xxxxguqs@53dxxxx2afe8301 -Dotel.exporter.otlp.endpoint=http://tracing-analysis-dc-bj:8090
- 如果您选择使用OpenTelemetry Collector转发,则需删除
-Dotel.exporter.otlp.headers=Authentication=<token>
并修改<endpoint>
为您本地部署的服务地址。
- 如果您选择直接上报数据,请将
- 启动应用。
在链路追踪Tracing Analysis控制台的应用列表页面选择目标应用,查看链路数据。