全部產品
Search
文件中心

Application Real-Time Monitoring Service:通過OpenTelemetry Java SDK為調用鏈增加自訂埋點

更新時間:Jun 06, 2026

接入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();
        }
    }
}

範例程式碼說明:

  1. OpenTelemetryControllerinit方法中啟動了一個定時調度任務,並在調度任務開始時建立一個Span,在調度任務結束時關閉該Span。

  2. OpenTelemetryControllerparent方法中調用了Opentelemetry SDK的多個方法。

    1. 每次被調用時建立一個名為parent的Span,並在方法結束時關閉該Span。

    2. 調用Baggage SDK,新加了名為user.iduser.name的兩對Baggage,這兩個Baggage後續會傳遞到下遊應用。

    3. 為步驟2.a建立的Span新增了兩個Attribute。

  3. OpenTelemetryControllerchild方法中會執行以下操作。

    1. 每次被調用時建立一個名為child的Span,並在方法結束時關閉改Span,需要注意,該Span是步驟2.a建立的Span的子Span。

    2. 調用擷取Trace內容相關的方法並列印traceId。

    3. 調用擷取Baggage的方法並列印步驟2.b中新增的Baggage。

不同探針差異點對比

對於上述代碼中的不同操作,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-sdkotel.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及其子Span child,Span詳情的Attribute中http.methodGEThttp.uri/parent,自訂屬性otel.scope.namemanual-sdk

3.x及以上版本探針

  • 步驟1埋點效果:

    在 ARMS 控制台的調用鏈詳情頁中,可查看埋點採集到的 Trace 資訊。左側瀑布圖展示 span 調用關係,例如應用 elastic-search-9schedule 方法被採集為一個 span,組件類型為 user_method,總回應時間 567ms。右側 Span Details 面板展示應用程式名稱、Span Name、IP、spanId、Status Code 等基礎資訊。下方 Attributes 地區分組展示附加屬性,包括 HTTP Information(http.pathhttp.status_code)、RPC Information(rpc.type)及 Buildin Information(component.name=user_methodslow=1 等)。

  • 步驟2.x、3.x埋點效果:

    通過OpenTelemetry SDK產生的Span和當前探針埋點Tomcat產生的Span在一條鏈路上,其中名為child的Span會作為名為parent的Span的方法棧存在,且通過OpenTelemetry SDK產生的Span的相關Attribute也設定成功。

    方法棧視圖中,最外層為 OpenTelemetry Entry Span,內部依次調用 parentchild,三層調用均耗時約1.01秒。child 方法的 Attributes 中可見 line=-1rpc.type=98

相關文檔

您可以在應用的業務日誌中關聯調用鏈的TraceId資訊,從而在應用出現問題時,能夠通過調用鏈的TraceId快速關聯到業務日誌,及時定位、分析解決問題。更多資訊,請參見Java應用業務日誌關聯調用鏈TraceId