天天看點

OpenTelemetry Java SDK 進階用法

作者:效率智能制造

通過引入 OpenTelemetry SDK,可以觀測業務核心邏輯,比如給核心的業務設定一個 span 以統計、跟蹤、分析其實際行為、設定業務屬性名額等。此方法具有一定的侵入性。

啟動指令

java -javaagent:../opentelemetry-javaagent/opentelemetry-javaagent.jar \
-Dotel.traces.exporter=otlp \
-Dotel.exporter.otlp.endpoint=http://192.168.91.11:4317 \
-Dotel.resource.attributes=service.name=demo,version=dev \
-Dotel.metrics.exporter=otlp \
-jar springboot-opentelemetry-otlp-server.jar --client=true
複制代碼           

根據啟動參數 exporter 的不同,sdk 方式也需要引入對應的 exporter ,否則啟動失敗。如啟動參數使用了 otlp 和 logging 兩個 exporter,則需要在 pom 添加對應的兩個 exporter 依賴。

如果沒有使用 sdk 相關依賴,則不需要做對應的調整。

引入依賴

<dependencies>
		...
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-exporter-otlp</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-extension-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-semconv</artifactId>
            <version>1.21.0-alpha</version>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
            <version>1.21.0-alpha</version>
        </dependency>
		...
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>io.opentelemetry</groupId>
				<artifactId>opentelemetry-bom</artifactId>
				<version>1.21.0</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
複制代碼           

建立 sdk

@Bean
    public OpenTelemetry openTelemetry() {
        return AutoConfiguredOpenTelemetrySdk.builder()
                .setResultAsGlobal(false)
                .build()
                .getOpenTelemetrySdk();
    }
複制代碼           

鍊路(Trace)

建立 Tracer

Tracer 主要是用來擷取、建立 span 對象。注意: Tracer 通常不負責進行配置,這是TracerProvider 的職責。 OpenTelemetry interface 提供了預設的TracerProvider實作。

TracerProvider getTracerProvider();
複制代碼           

通過openTelemetry() 來擷取 Tracer 對象

@Bean
    public Tracer tracer() {
        return openTelemetry().getTracer(appName);
    }

複制代碼           

建立 Span

除了 Tracer 外,不準許任何其他 API 建立 Span 。

Span span = tracer.spanBuilder(spanName).startSpan();
複制代碼           

擷取目前 Span 對象

擷取目前 span 對象,可以為目前 span 設定 attribute、event 等。

Span span = Span.current();
複制代碼           

建立 Attribute

attribute 為 span 的屬性,為目前 span 的标簽。

span.setAttribute(key, value);
複制代碼           

建立 Link Span

一個 Span 可以連接配接一個或多個因果相關的其他 Span 。連結可用于表示批處理操作,其中一個 Span 的初始化由多個 Span 初始化構成,其中每個 Span 表示批進行中處理的單個輸入項。

Span child = tracer.spanBuilder(spanName)
		.addLink(span(1))
		.addLink(span(2))
		.addLink(span(3))
		.startSpan();
複制代碼           

建立 Event

Span 可以攜帶零個或多個 Span 屬性的命名事件進行注釋,每一個事件都是一個 key:value 鍵值對并自動攜帶相應的時間戳。

span.addEvent(eventName);

span.addEvent(eventName,Attributes);
複制代碼           

recordException 是 addEvent 的特殊變體,用于記錄異常事件。

建立一個嵌套的 Span

通過 setParent(parentSpan) 設定 parentSpan

void parent() {
	Span parentSpan = tracer.spanBuilder("parent")
		.startSpan();
	childSpan(parentSpan);
	parentSpan.end();
}
void childSpan(Span parentSpan) {
	Span childSpan = tracer.spanBuilder("childSpan")
		.setParent(parentSpan)
		.startSpan();
	// do stuff
	childSpan.end();
}
複制代碼           

Baggage 用法

Baggage 可以在整個鍊路傳播,适用于全局埋點,比如使用者id埋點、使用者名埋點,以便追蹤業務資料。

gateway方法 set Baggage

// Baggage 用法,此處set
	Baggage.current().toBuilder().put("app.username", "gateway").build().makeCurrent();
	logger.info("gateway set baggage[app.username] value: gateway");
複制代碼           

resource 方法 get Baggage

// Baggage 用法,此get
	String baggage = Baggage.current().getEntryValue("app.username");
	logger.info("resource get baggage[app.username] value: {}", baggage);
複制代碼           

通過已知的traceId和spanId,來構造一個新span。

Tracer 構造 span 提供了setParent(context)方法,便于為自定義的 span 構造一個父 span。

tracer.spanBuilder(spanName).setParent(context)
複制代碼           

其中 setParent 需要傳入 Context 參數,是以需要構造一個上下文。

而 OpenTelemetry sdk 隻提供了一個 create 方法用于建立 SpanContext,其中可以自定義 traceId 和 spanId。

SpanContext create(String traceIdHex, String spanIdHex, TraceFlags traceFlags, TraceState traceState)
複制代碼           

SpanContext 作為表示 Span 的一部分,他必須可以進行序列化,并沿着分布式上下文進行傳播。SpanContext 是不可變的。

OpenTelemetry SpanContext 符合 W3C TraceContext 規範。這包含兩個辨別符 - TraceId 和 SpanId - 一套通用 TraceFlags 和 系統特定 TraceState。

  1. TraceId 一個有效的 TraceId 是一個 16 位元組的數組,且至少有一個非零位元組。
  2. SpanId 一個有效的 SpanId 是一個 8 位元組的數組,且至少有一個非零位元組。
  3. TraceFlags 包含該 trace 的詳情。不像 TraceFlags,TraceFlags 影響所有的 traces。目前版本和定義的 Flags 隻有 sampled 。
  4. TraceState 攜帶特定 trace 辨別資料,通過一個 KV 對數組進行辨別。TraceState允許多個跟蹤系統參與同一個 Trace。完整定義請參考 W3C Trace Context specification 。

本 API 必須實作建立 SpanContext 的方法。這些方法應當是唯一的方法用于建立 SpanContext。這個功能必須在 API 中完全實作,并且不應當可以被覆寫。

但 SpanContext 并不是 Context,因而還需要做一層轉換。

private Context withSpanContext(SpanContext spanContext, Context context) {
	return context.with(Span.wrap(spanContext));
}
複制代碼           

完整代碼如下:

/***
     * @Description 通過已知的traceId和spanId,來構造一個新span。
     * @Param [spanName, traceId, spanId]
     * @return java.lang.String
     **/
    @GetMapping("/customSpanByTraceIdAndSpanId")
    @ResponseBody
    public String customSpanByTraceIdAndSpanId(String spanName,String traceId,String spanId){
        assert StringUtils.isEmpty(spanName):"spanName 不能為空";
        assert StringUtils.isEmpty(traceId):"traceId 不能為空";
        assert StringUtils.isEmpty(spanId):"spanId 不能為空";
        Context context =
                withSpanContext(
                        SpanContext.create(
                                traceId, spanId, TraceFlags.getSampled(), TraceState.getDefault()),
                        Context.current());
        Span span = tracer.spanBuilder(spanName)
                .setParent(context)
                .startSpan();
        span.setAttribute("attribute.a2", "some value");
        span.setAttribute("func","attr");
        span.setAttribute("app","otel3");
        span.end();
        return buildTraceUrl(span.getSpanContext().getTraceId());
    }

    private Context withSpanContext(SpanContext spanContext, Context context) {
        return context.with(Span.wrap(spanContext));
    }
複制代碼           

需要特别注意,依據目前測試寫法的請求自身會産生一個新的 trace 資訊。新構造的 span 是依據傳入的參數進行構造。

我們可以通過通路連結來觀察結果

http://localhost:8080/customSpanByTraceIdAndSpanId?spanName=tSpan&traceId=24baeeddfbb35fceaf4c18e7cae58fe1&spanId=ff1955b4f0eacc4f

名額(Metrics)

OpenTelemetry 還提供了 metric 相關操作的 API。

span 提供關于應用程式的詳細資訊,但生成的資料與系統上的負載成正比。相比之下,度量将單個度量組合成聚合,并生成作為系統負載函數的常量資料。聚合缺乏診斷低級問題所需的細節,但是通過幫助識别趨勢和提供應用程式運作時遙測來補充跨度。

度量 API 定義了各種工具。儀器記錄測量值,這些測量值由度量 SDK 聚合,并最終導出到程序外。儀器有同步和異步兩種。同步儀器記錄測量結果。異步儀器注冊回調,每次采集調用一次,并記錄該時間點的測量值。下列儀器可供選擇:

  1. LongCounter/DoubleCounter: 隻記錄正數值,有同步和異步選項。用于計算事物,例如通過網絡發送的位元組數。預設情況下,計數器測量被聚合為始終遞增的單調和。
  2. LongUpDownCounter/DoubleUpDownCounter: 記錄正負值,有同步和異步選項。對于計算上升和下降的東西很有用,比如隊列的大小。預設情況下,向上向下計數器測量被聚合為非單調和。
  3. LongGauge/DoubleGauge: 用異步回調測量瞬時值。用于記錄不能跨屬性合并的值,如 CPU 使用率百分比。預設情況下,量規測量被聚合為量規。
  4. LongHistogram/DoubleHistogram(長直方圖/雙直方圖): 記錄對直方圖分布分析最有用的測量值。沒有異步選項可用。用于記錄 HTTP 伺服器處理請求所花費的時間等。預設情況下,直方圖測量被聚合為顯式的桶直方圖。

擷取 Meter 對象

API定義了一個 Meter 接口。該接口由一組 instrument 構造器,和一個使用原子方式批量擷取測量值的工具組成。Meter 可以通過 MeterProvider 的 getMeter(name)方法來建立一個新執行個體。 MeterProvider 通常被期望作為單例來使用。 其實作應作為 MeterProvider 全局的唯一實作。通過 Meter 對象可以建構不同類型的 metric。

@Bean
    public Meter meter() {
        return openTelemetry().getMeter(appName);
    }
複制代碼           

這裡跳過了 MeterProvider 的細節,主要原因在于 OpenTelemetry Interface 提供了 MeterProvider 的預設 noop 實作。

default MeterProvider getMeterProvider() {
        return MeterProvider.noop();
    }
複制代碼           

建構 gauge 類型的 metric

meter().gaugeBuilder("connections")
		.setDescription("目前Socket.io連接配接數")
		.setUnit("1")
		.buildWithCallback(
				result -> {
					System.out.println("metrics");
					for (int i = 1; i < 4; i++) {
						result.record(
								i,
								Attributes.of(
										AttributeKey.stringKey("id"),
										"a" + i));
					}
				});

複制代碼           

buildWithCallback 是一個回調函數,是支援異步 API 并按需收集度量資料的附加工具,按間隔收集資料,預設 1min 一次。

原文連結:https://juejin.cn/post/7199833312579321912

繼續閱讀