日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

通過OpenTelemetry Java SDK為調用鏈增加自定義埋點

接入ARMS應用監控以后,ARMS探針對常見的Java框架進行了自動埋點,因此不需要修改任何代碼,就可以實現調用鏈信息的采集。如果您需要在調用鏈信息中,體現業務方法的執行情況,可以引入OpenTelemetry Java SDK,在業務代碼中增加自定義埋點。

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>

ARMSOpenTelemetry埋點的兼容

ARMSOpenTelemetry埋點的兼容性介紹涉及以下名詞,OpenTelemetry相關的其他名稱解釋,請參見OpenTelemetry Specification

  • Span:一次請求的一個具體操作,比如遠程調用入口或者內部方法調用。

  • SpanContext:一次請求追蹤的上下文,用于關聯該次請求下的具體操作。

  • Attribute:Span的附加屬性字段,用于記錄關鍵信息。

OpenTelemetrySpan可以分為三類:

  • 入口Span:會創建新的SpanContext,例如Server、Consumer。

    說明

    對于此類Span,ARMS的埋點入口多位于框架內部,手動埋點時鏈路上下文已存在,ARMS會將OpenTelemetry的入口Span作為內部Span處理。對于ARMS沒有埋點的入口,則OpenTelemetry的入口Span保持不變,例如異步調用或者自定義RPC框架入口,同時ARMS會在客戶端聚合,生成相關統計數據。

  • 內部Span:會復用已經創建的SpanContext,作為內部方法棧記錄。

  • 出口Span:會將SpanContext透傳下去,例如Client、Producer。

目前,ARMS對于入口Span和內部Span做了兼容。對于出口Span,ARMS暫不支持按照OpenTelemetry標準透傳,而是按照ARMS自定義格式透傳。

例如以下代碼:

重要

以下代碼片段需要注意,最終獲取OpenTelemetry實例需要通過調用GlobalOpenTelemetry.get()方法獲取,不能直接使用上一步通過OpenTelemetry SDK手動構建的Opentelemetry實例。否則會導致在4.x版本探針中無法看到通過SDK埋點生成的Span數據。

package com.alibaba.arms.brightroar.console.controller;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/ot")
public class OpenTelemetryController {

    private Tracer tracer;

    private ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

    @PostConstruct
    public void init() {
		OpenTelemetry openTelemetry = 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.builder()
                    .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()) {
            span.setAttribute("user.id", Baggage.current().getEntryValue("user.id"));
            span.addEvent("Sleep Start");
            Thread.sleep(1000);
            Attributes attr = Attributes.of(AttributeKey.longKey("cost"), 1000L);
            span.addEvent("Sleep End", attr);
        } catch (Throwable e) {
            span.setStatus(StatusCode.ERROR, e.getMessage());
        } finally {
            span.end();
        }
    }

}

以上示例代碼中通過OpenTelemetry SDK創建了三個Span:

  • parent:按照OpenTelemetry標準是HTTP入口,但是由于ARMSTomcat內置代碼中已經創建了鏈路上下文,因此這里會作為一個內部方法記錄在ARMS方法棧上。

  • childparent Span的內部Span,作為內部方法記錄在方法棧上。

  • schedule:獨立線程入口Span,默認情況下ARMS沒有為此類入口創建上下文,因此這里會作為一個自定義方法入口,并生成相應的統計數據。

使用OpenTelemetry Baggage API透傳業務自定義標簽

OpenTelemetry中,Baggage可以作為上下文信息在Span之間傳遞,通過向Baggage設置鍵值對,透傳業務自定義標簽。Baggage存儲在HTTP Header中并通過HTTP Header進行傳播,因此不應在Baggage中存儲敏感數據。

以上示例代碼的Parent Span先在Baggage中存儲兩個鍵值對,然后在Child Span中獲取Baggage中存儲的值。

獲取Trace ID

SpanContext中包含Trace IDSpan ID等信息,Trace ID可以通過Span.current().getSpanContext().getTraceId()方法獲得。

ARMS控制臺查看parentchild

ARMS控制臺找到/ot/parentHTTP入口的內部方法棧,可以看到多了以下Span的展示。更多信息,請參見調用鏈分析

OTel埋點內部Span

ARMS控制臺查看schedule

ARMS控制臺支持通過以下幾個頁面查看:

  • 可以在ARMS控制臺應用總覽頁面看到自定義入口的統計。更多信息,請參見應用總覽OTel埋點出口Span

  • 可以在接口調用頁面查看Span詳細信息。更多信息,請參見接口調用Otel接口調用頁面

  • 可以在調用鏈路頁面看到獨立的入口。更多信息,請參見調用鏈分析OTel埋點調用鏈查詢

    單擊放大鏡圖標,可以查看入口的詳細信息。Otel埋點調用鏈詳情

    目前ARMS支持將OpenTelemetry SpanAttribute作為Tags展示,單擊Span名稱可看到SpanAttribute。

    Span的Attribute

異步上下文傳遞

說明

4.x及以上探針版本支持。

下方是一個簡單的生產者消費者模式代碼示例,在生產者中生產事件時,將生產者線程的Trace上下文記錄到事件中,在消費事件時,取出上下文并還原。

class Event {
    private Context context;
    private String msg;

    public Event(Context context, String msg) {
        this.context = context;
        this.msg = msg;
    }
}

private final LinkedBlockingQueue<Event> linkedBlockingQueue = new LinkedBlockingQueue<Event>();

public void produce(String msg) {
    linkedBlockingQueue.add(new Event(Context.current(), msg));
}

public void consume() throws Exception {
    Event event = linkedBlockingQueue.take();
    try(Scope scope = event.context.makeCurrent()) {
        processEvent(event);
    }
}

public void processEvent(Event event) {
    //todo process event
}

相關文檔

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