接入ARMS應用監控以後,ARMS探針對常見的Java架構進行了自動埋點,因此不需要修改任何代碼,就可以實現調用鏈資訊的採集。如果您需要在調用鏈資訊中,體現業務方法的執行情況,可以引入OpenTelemetry Java SDK,在業務代碼中增加自訂埋點。本文介紹如何通過OpenTelemetry Java SDK實現自訂埋點、擷取Trace上下文資訊、自訂Baggage以及自訂Attribute。
ARMS探針支援的組件和架構,請參見ARMS應用監控支援的Java組件和架構。
前提條件
引入依賴
請先參考如下Maven代碼引入OpenTelemetry Java SDK。更多資訊,請參見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-sdk</artifactId>
</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>
ARMS對OpenTelemetry埋點的相容
名詞介紹
此處只介紹一些常用的名詞,其他名稱解釋,請參見OpenTelemetry Specification。
-
Span:一次請求的一個具體操作,比如遠程調用入口或者內部方法調用。
-
SpanContext:一次請求追蹤的上下文,包含traceId、spanId等資訊。
-
Attribute:Span的附加屬性欄位,用於記錄關鍵資訊。
-
Baggae:在整條鏈路上透傳的一些Key、Value資訊。
使用OpenTelemetry Java SDK
通過OpenTelemetry的SDK主要可以實現以下操作:
-
埋點產生Span。
-
為Span增加Attributes。
-
在鏈路上下文透傳Baggage。
-
擷取當前Trace上下文並列印traceId、spanId等。
此處通過一段範例程式碼示範如何使用OpenTelemetry SDK完成上述操作。
以下程式碼片段需要注意,最終擷取OpenTelemetry執行個體需要通過調用GlobalOpenTelemetry.get()方法擷取,不能直接使用上一步通過OpenTelemetry SDK手動構建的Opentelemetry執行個體。否則會導致在4.x版本探針中無法看到通過SDK埋點產生的Span資料。
@RestController
@RequestMapping("/ot")
public class OpenTelemetryController {
private Tracer tracer;
private ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
@PostConstruct
public void init() {
OpenTelemetrySdk.builder()
.setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
.buildAndRegisterGlobal();
tracer = GlobalOpenTelemetry.get().getTracer("manual-sdk", "1.0.0");
ses.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Span span = tracer.spanBuilder("schedule")
.setAttribute("schedule.time", System.currentTimeMillis())
.startSpan();
try (Scope scope = span.makeCurrent()) {
System.out.println("scheduled!");
Thread.sleep(500L);
span.setAttribute("schedule.success", true);
System.out.println(Span.current().getSpanContext().getTraceId()); // 擷取 TraceId
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR, t.getMessage());
} finally {
span.end();
}
}
}, 10, 30, TimeUnit.SECONDS);
}
@ResponseBody
@RequestMapping("/parent")
public String parent() {
Span span = tracer.spanBuilder("parent").setSpanKind(SpanKind.SERVER).startSpan();
try (Scope scope = span.makeCurrent()) {
// 使用Baggage透傳業務自訂標籤
Baggage baggage = Baggage.current().toBuilder()
.put("user.id", "1")
.put("user.name", "name")
.build();
try (Scope baggageScope = baggage.storeInContext(Context.current()).makeCurrent()) {
child();
}
span.setAttribute("http.method", "GET");
span.setAttribute("http.uri", "/parent");
} finally {
span.end();
}
return "parent";
}
private void child() {
Span span = tracer.spanBuilder("child").startSpan();
try (Scope scope = span.makeCurrent()) {
System.out.println("current traceId = " + Span.current().getSpanContext().getTraceId());
System.out.println("userId in baggage = " + Baggage.current().getEntryValue("user.id"));
Thread.sleep(1000);
} catch (Throwable e) {
span.setStatus(StatusCode.ERROR, e.getMessage());
} finally {
span.end();
}
}
}
範例程式碼說明:
-
在
OpenTelemetryController的init方法中啟動了一個定時調度任務,並在調度任務開始時建立一個Span,在調度任務結束時關閉該Span。 -
在
OpenTelemetryController的parent方法中調用了Opentelemetry SDK的多個方法。-
每次被調用時建立一個名為
parent的Span,並在方法結束時關閉該Span。 -
調用Baggage SDK,新加了名為
user.id和user.name的兩對Baggage,這兩個Baggage後續會傳遞到下遊應用。 -
為步驟2.a建立的Span新增了兩個Attribute。
-
-
在
OpenTelemetryController的child方法中會執行以下操作。
不同探針差異點對比
對於上述代碼中的不同操作,ARMS 3.x及以下版本探針和ARMS 4.x探針的支援情況有所區別,詳情可見下方表格。
|
步驟 |
4.x及以上版本探針 |
3.x及以下版本探針 |
|
1 |
支援,會產生一個新的Span。 |
支援,會產生一個新的Span。 |
|
2.1 |
支援 |
支援 |
|
2.2 |
支援 |
不支援 |
|
2.3 |
支援 |
支援 |
|
3.1 |
支援 |
支援,該Span會作為步驟2.a建立出來的Span的方法棧存在。 |
|
3.2 |
支援,和ARMS中traceId是一個。 |
不支援,列印出的traceId和ARMS探針中的traceId不同。 |
|
3.2 |
支援 |
支援 |
埋點效果展示
4.x及以上版本
-
步驟1埋點效果:
可以正常看到通過OpenTelemetry SDK產生的Span。
在ARMS鏈路追蹤的Span詳情中,可查看應用程式名稱(如
elastic-search-8)、介面名(如schedule)、Span類型(INTERNAL)、耗時等基本資料。在Attributes標籤頁中可確認OpenTelemetry相關屬性,例如otel.scope.name=manual-sdk、otel.scope.version=1.0.0,以及業務自訂屬性如schedule.success=true等,驗證埋點已生效。 -
步驟2.x、3.x埋點效果:
通過OpenTelemetry SDK產生的Span和當前探針埋點Tomcat產生的Span在一條鏈路上,且通過OpenTelemetry SDK產生的Span的相關Attribute也設定成功。
Trace詳情中,Tomcat探針埋點產生的Span介面名為
/opentelemetry/parent,OpenTelemetry SDK產生的Span介面名為parent及其子Spanchild,Span詳情的Attribute中http.method為GET、http.uri為/parent,自訂屬性otel.scope.name為manual-sdk。
3.x及以上版本探針
-
步驟1埋點效果:
在 ARMS 控制台的調用鏈詳情頁中,可查看埋點採集到的 Trace 資訊。左側瀑布圖展示 span 調用關係,例如應用
elastic-search-9的schedule方法被採集為一個 span,組件類型為user_method,總回應時間 567ms。右側 Span Details 面板展示應用程式名稱、Span Name、IP、spanId、Status Code 等基礎資訊。下方 Attributes 地區分組展示附加屬性,包括 HTTP Information(http.path、http.status_code)、RPC Information(rpc.type)及 Buildin Information(component.name=user_method、slow=1等)。 -
步驟2.x、3.x埋點效果:
通過OpenTelemetry SDK產生的Span和當前探針埋點Tomcat產生的Span在一條鏈路上,其中名為
child的Span會作為名為parent的Span的方法棧存在,且通過OpenTelemetry SDK產生的Span的相關Attribute也設定成功。在方法棧視圖中,最外層為 OpenTelemetry Entry Span,內部依次調用 parent 和 child,三層調用均耗時約1.01秒。child 方法的 Attributes 中可見
line=-1、rpc.type=98。
相關文檔
您可以在應用的業務日誌中關聯調用鏈的TraceId資訊,從而在應用出現問題時,能夠通過調用鏈的TraceId快速關聯到業務日誌,及時定位、分析解決問題。更多資訊,請參見Java應用業務日誌關聯調用鏈TraceId。