通過OpenTelemetry為應用埋點并上報鏈路數據至可觀測鏈路 OpenTelemetry 版后,可觀測鏈路 OpenTelemetry 版即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控數據。本文介紹如何使用OpenTelemetry Java Agent/SDK進行自動或手動埋點并上報數據。
前提條件
登錄ARMS控制臺,在左側導航欄單擊接入中心。
在服務端應用區域單擊OpenTelemetry卡片。
在彈出的OpenTelemetry面板中選擇數據需要上報的地域。
說明初次接入的地域將會自動進行資源初始化。
選擇連接方式和上報方式,然后復制接入點信息。
連接方式:若您的服務部署在阿里云上,且所屬地域與選擇的接入地域一致,推薦使用阿里云內網方式,否則選擇公網方式。
上報方式:根據客戶端支持的協議類型選擇HTTP或gRPC協議上報數據。
背景信息
OpenTelemetry Java Agent支持自動埋點的Java框架列表如下,完整信息請參考Supported Libraries and Versions。
框架 | 框架版本限制 |
2.5+ | |
10.0+ | |
1.6+ | |
2.20+(暫不支持3.x) | |
2.0+ | |
3.2+ | |
3.0+ | |
2.7+ | |
4.1+ | |
2.0+ | |
0.11+ | |
0.11+ | |
1.2+(暫不支持3.x) | |
2.8+ | |
5.0+ | |
4.8+ | |
2.3+ | |
5.4+ | |
8.0+ | |
1.3+ | |
1.9+ | |
1.0+ | |
1.11.x和2.2+ | |
1.14+ | |
3.0+ | |
2.0+和3.1+ | |
0.9.2+ | |
4.0+(默認禁用) | |
0.7+ | |
2.3+ | |
2.0+(暫不支持3.x) | |
9.2+(暫不支持10+) | |
2.2+ | |
1.2+(暫不支持3.x) | |
7.16+和8.0+ | |
5.0+ | |
5.0+ | |
2.9+ | |
1.4+ | |
1.19+ | |
3.0+ | |
12.0+ | |
1.6+ | |
10.0+ | |
2.0+ | |
3.3+ | |
1.0+ | |
3.0+ | |
Java 8+ | |
1.4+ | |
Java 8+ | |
Java 11+ | |
Java 8+ | |
Java 8+ | |
0.5+ | |
1.1+ | |
2.0+(暫不包含3.x) | |
1.1+ | |
Java 8+ | |
1.4+ | |
1.1+ | |
4.2+ | |
2.3+ | |
1.0+ | |
1.0+ | |
7.0+ | |
4.0+ | |
1.2+ | |
2.11+ | |
1.0+ | |
1.5+ | |
3.1+ | |
3.8+ | |
2.2+ | |
11.2+ | |
5.3.1+ | |
2.4+ | |
1.0+ | |
2.0+ | |
1.0+ | |
2.7+ | |
1.4+ | |
3.1+ | |
0.9+ | |
1.8+ | |
3.0+ | |
3.0+ | |
1.0+ | |
Java 8+ | |
1.0+ | |
2.8+ | |
2.2+ | |
2.3+ | |
- | |
3.0+(暫不支持5.0+) | |
2.0+ | |
1.8+ | |
4.1+(暫不支持6.0+) | |
2.0+ | |
2.7+ | |
1.0+ | |
3.1+ | |
3.1+ | |
3.1+ | |
2.0+ | |
5.3+ | |
2.12+ | |
8.5+ | |
6.6+(暫不支持8.x) | |
1.4+ | |
14.2+ | |
3.0+ | |
3.0+ | |
3.6+ | |
3.5+ | |
4.0+ | |
11.0+ | |
2.0+ |
示例Demo
示例代碼倉庫地址:java-opentelemetry-demo
方法一:使用OpenTelemetry Java Agent自動埋點
OpenTelemetry Java Agent提供了無侵入的接入方式,支持上百種Java框架自動上傳Trace數據,詳細的Java框架列表,請參見Supported Libraries and Versions。
下載Java Agent。
通過修改Java啟動的VM參數上報鏈路數據。
如果您選擇直接上報數據,請將
<token>
、<endpoint>
替換為前提條件中獲取的信息。說明Http方式不需要設置鑒權Token,僅需設置接入點(endpoint)。
Http方式
java -javaagent:/path/to/opentelemetry-javaagent.jar //請將路徑修改為您文件下載的實際地址。 -Dotel.exporter.otlp.protocol=http/protobuf \ -Dotel.exporter.otlp.traces.endpoint=<traces.endpoint> \ //替換為前提條件中獲取到的trace接入點。 -Dotel.exporter.otlp.metrics.endpoint=<metrics.endpoint> \ //替換為前提條件中獲取到的metric接入點。 -Dotel.logs.exporter=none \ -jar /path/to/your/app.jar
例如:
java -javaagent:/path/to/opentelemetry-javaagent.jar \ -Dotel.exporter.otlp.protocol=http/protobuf \ -Dotel.exporter.otlp.traces.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/traces \ -Dotel.exporter.otlp.metrics.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com/adapt_ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe****/api/otlp/metrics \ -Dotel.logs.exporter=none \ -jar /path/to/your/app.jar
gRPC方式
java -javaagent:/path/to/opentelemetry-javaagent.jar \ //請將路徑修改為您文件下載的實際地址。 -Dotel.exporter.otlp.protocol=grpc \ -Dotel.exporter.otlp.headers=Authentication=<token> \ //替換為前提條件中獲取到的鑒權Token。 -Dotel.exporter.otlp.endpoint=<endpoint> \ //替換為前提條件中獲取到的接入點。 -Dotel.logs.exporter=none \ -jar /path/to/your/app.jar
例如:
java -javaagent:/path/to/opentelemetry-javaagent.jar \ -Dotel.exporter.otlp.protocol=grpc \ -Dotel.exporter.otlp.headers=Authentication=ggxw4l****@7323a5caae3****_ggxw4l****@53df7ad2afe**** \ -Dotel.exporter.otlp.endpoint=http://tracing-analysis-dc-hz-internal.aliyuncs.com:8090 \ -Dotel.logs.exporter=none \ -jar /path/to/your/app.jar
說明如果您選擇使用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 -- "); } }
啟動應用。
登錄ARMS控制臺后,在 頁面選擇目標應用,查看鏈路數據。
說明語言列顯示圖標的應用為接入應用監控的應用,顯示-圖標的應用為接入可觀測鏈路 OpenTelemetry 版的應用。
方法三:同時使用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中。<dependencies> <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); // 第一種:自動埋點,基于 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參數上報鏈路數據。
-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>
為您本地部署的服務地址。
啟動應用。
登錄ARMS控制臺后,在 頁面選擇目標應用,查看鏈路數據。
說明語言列顯示圖標的應用為接入應用監控的應用,顯示-圖標的應用為接入可觀測鏈路 OpenTelemetry 版的應用。