通过OpenTelemetry为应用埋点并上报链路数据至云监控2.0后,云监控2.0即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTelemetry Java Agent/SDK进行自动或手动埋点并上报数据。
背景信息
OpenTelemetry Java Agent支持自动埋点的Java框架列表如下,完整信息请参考Supported Libraries and Versions。
步骤一:获取接入点信息
登录云监控2.0控制台,选择目标工作空间,在左侧导航栏选择 。
在服务端应用区域单击Java卡片,然后选择运行环境类型为手动安装,选择接入协议类型为Opentelemetry。
在参数配置区域单击LicenseKey右侧的点击获取,然后根据需求选择埋点方式、连接方式、上报方式,并输入应用名称、版本号和部署环境。
页面下方将会根据配置参数生成相应的接入代码,代码中包含了Endpoint、LicenseKey等接入点信息。
步骤二:设置依赖库
自动埋点(推荐)
OpenTelemetry Java Agent提供了无侵入的接入方式,支持上百种Java框架自动上传Trace数据,详细的Java框架列表,请参见Supported Libraries and Versions。
下载Java Agent,推荐下载最新版本。
通过修改Java启动的VM参数上报链路数据。
下述代码将直接运行您的应用并加载 OpenTelemetry Java Agent,请将步骤一获取到的接入点信息替换到以下代码中,并将
/path/to/opentelemetry-javaagent.jar
和/path/to/your/app.jar
分别替换为 Java Agent 地址以及您的 Jar 包地址。java -javaagent:/path/to/opentelemetry-javaagent.jar \ -Dotel.resource.attributes=service.name=<service name>,acs.cms.workspace=<workspace>,service.version=<service version>,deployment.environment=<environment> \ -Dotel.exporter.otlp.protocol=http/protobuf \ -Dotel.exporter.otlp.traces.endpoint=<traces.endpoint> \ -Dotel.exporter.otlp.metrics.endpoint=<metrics.endpoint> \ -Dotel.exporter.otlp.headers="x-arms-license-key=<license-key>,x-arms-project=<arms-project>,x-cms-workspace=<workspace>" \ -Dotel.logs.exporter=none \ -jar /path/to/your/app.jar
说明产生测试数据:请访问您的服务以产生数据,若您使用的是测试Jar包,可以通过访问本地的8087端口来产生测试数据。
运行程序以生成并上报 Trace 数据。
手动埋点
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.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>
获取OpenTelemetry Tracer。
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.http.trace.OtlpHttpSpanExporter; 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, "<service name>", // 应用名。 ResourceAttributes.SERVICE_VERSION, "<service version>", // 版本号。 ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "<environment>", // 部署环境。 ResourceAttributes.HOST_NAME, "${host-name}", // 请将 ${host-name} 替换为您的主机名。 AttributeKey.stringKey("acs.cms.workspace"), "<workspace>" // 请将 <workspace> 替换为您的workspace名称。 ))); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(BatchSpanProcessor.builder(OtlpHttpSpanExporter.builder() .setEndpoint("<endpoint>") // 请将<endpoint>替换为步骤一中获取的接入点信息。 .addHeader("x-arms-license-key", "<license-key>") // 请将 <license-key> 替换为步骤一中获取的接入点信息。 .addHeader("x-arms-project", "<arms-project>") // 请将 <arms-project> 替换为步骤一中获取的接入点信息。 .addHeader("x-cms-workspace", "<workspace>") // 请将 <workspace> 替换为您的workspace名称。 .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; } }
创建 Span。
import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.context.Scope; public class Main { public static void parentMethod() { Span span = OpenTelemetrySupport.getTracer().spanBuilder("parent span").startSpan(); try (Scope scope = span.makeCurrent()) { span.setAttribute("good", "job"); childMethod(); } catch (Throwable t) { span.setStatus(StatusCode.ERROR, "handle parent span error"); } finally { span.end(); } } public static void childMethod() { Span span = OpenTelemetrySupport.getTracer().spanBuilder("child span").startSpan(); try (Scope scope = span.makeCurrent()) { span.setAttribute("hello", "world"); } catch (Throwable t) { span.setStatus(StatusCode.ERROR, "handle child span error"); } finally { span.end(); } } public static void main(String[] args) { parentMethod(); } }
运行程序以生成并上报 Trace 数据。
自动埋点+手动埋点
您可以在使用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中。获取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); // 第一种:自动埋点,基于 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"; } // 第二种:通过注解创建埋点 @WithSpan private void child(@SpanAttribute("user.type") String userType) { System.out.println(userType); biz(); } // 第三种:获得 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(); } } }
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 -- "); } }
通过修改Java启动的VM参数上报链路数据。
将步骤一获取到的接入点信息替换到以下代码中,并将
/path/to/opentelemetry-javaagent.jar
和/path/to/your/app.jar
分别替换为 Java Agent 地址以及您的 Jar 包地址。java -javaagent:/path/to/opentelemetry-javaagent.jar \ -Dotel.resource.attributes=service.name=<service name>,acs.cms.workspace=<workspace>,service.version=<service version>,deployment.environment=<environment> \ -Dotel.exporter.otlp.protocol=http/protobuf \ -Dotel.exporter.otlp.traces.endpoint=<traces.endpoint> \ -Dotel.exporter.otlp.metrics.endpoint=<metrics.endpoint> \ -Dotel.exporter.otlp.headers="x-arms-license-key=<license-key>,x-arms-project=<arms-project>,x-cms-workspace=<workspace>" \ -Dotel.logs.exporter=none \ -jar /path/to/your/app.jar
运行程序以生成并上报 Trace 数据。