OpenTelemetry?Java?SDK?高級用法解析
引言
通過引入 OpenTelemetry SDK,可以觀測業(yè)務(wù)核心邏輯,比如給核心的業(yè)務(wù)設(shè)置一個 span 以統(tǒng)計、跟蹤、分析其實際行為、設(shè)置業(yè)務(wù)屬性指標(biāo)等。此方法具有一定的侵入性。
啟動命令
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
根據(jù)啟動參數(shù) exporter 的不同,sdk 方式也需要引入對應(yīng)的 exporter ,否則啟動失敗。如啟動參數(shù)使用了 otlp
和 logging
兩個 exporter,則需要在 pom 添加對應(yīng)的兩個 exporter 依賴。
如果沒有使用 sdk 相關(guān)依賴,則不需要做對應(yīng)的調(diào)整。
引入依賴
<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>
創(chuàng)建 sdk
/** * @Description 推薦使用這種方式。<br> * 這種方式的背后提供的是 noop,也就是默認的 provider,會根據(jù)啟動參數(shù)來決定自行構(gòu)造 所需的 provider。 * @return io.opentelemetry.api.OpenTelemetry **/ @Bean public OpenTelemetry openTelemetry() { return AutoConfiguredOpenTelemetrySdk.builder() .setResultAsGlobal(false) // 這里填寫false,否則會與全局javaagent 沖突 .build() .getOpenTelemetrySdk(); }
鏈路(Trace)
創(chuàng)建 Tracer
Tracer
主要是用來獲取、創(chuàng)建 span 對象。注意: Tracer
通常不負責(zé)進行配置,這是TracerProvider
的職責(zé)。 OpenTelemetry interface 提供了默認的TracerProvider
實現(xiàn)。
TracerProvider getTracerProvider();
通過openTelemetry()
來獲取 Tracer
對象
@Bean public Tracer tracer() { return openTelemetry().getTracer(appName); }
創(chuàng)建 Span
除了 Tracer 外,不準(zhǔn)許任何其他 API 創(chuàng)建 Span 。
Span span = tracer.spanBuilder(spanName).startSpan();
獲取當(dāng)前 Span 對象
獲取當(dāng)前 span 對象,可以為當(dāng)前 span 設(shè)置 attribute、event 等。
Span span = Span.current();
創(chuàng)建 Attribute
attribute 為 span 的屬性,為當(dāng)前 span 的標(biāo)簽。
span.setAttribute(key, value);
創(chuàng)建 Link Span
一個 Span 可以連接一個或多個因果相關(guān)的其他 Span 。鏈接可用于表示批處理操作,其中一個 Span 的初始化由多個 Span 初始化構(gòu)成,其中每個 Span 表示批處理中處理的單個輸入項。
Span child = tracer.spanBuilder(spanName) .addLink(span(1)) .addLink(span(2)) .addLink(span(3)) .startSpan();
創(chuàng)建 Event
Span 可以攜帶零個或多個 Span 屬性的命名事件進行注釋,每一個事件都是一個 key:value 鍵值對并自動攜帶相應(yīng)的時間戳。
span.addEvent(eventName); span.addEvent(eventName,Attributes);
recordException 是 addEvent
的特殊變體,用于記錄異常事件。
創(chuàng)建一個嵌套的 Span
通過 setParent(parentSpan) 設(shè)置 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埋點、用戶名埋點,以便追蹤業(yè)務(wù)數(shù)據(jù)。
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,來構(gòu)造一個新span。
Tracer 構(gòu)造 span 提供了setParent(context)
方法,便于為自定義的 span 構(gòu)造一個父 span。
tracer.spanBuilder(spanName).setParent(context)
其中 setParent
需要傳入 Context
參數(shù),所以需要構(gòu)造一個上下文。
而 OpenTelemetry sdk 只提供了一個 create
方法用于創(chuàng)建 SpanContext
,其中可以自定義 traceId 和 spanId。
SpanContext create(String traceIdHex, String spanIdHex, TraceFlags traceFlags, TraceState traceState)
SpanContext
作為表示 Span 的一部分,他必須可以進行序列化,并沿著分布式上下文進行傳播。SpanContext
是不可變的。
OpenTelemetry SpanContext
符合 W3C TraceContext 規(guī)范。這包含兩個標(biāo)識符 - TraceId 和 SpanId - 一套通用 TraceFlags 和 系統(tǒng)特定 TraceState。
TraceId
一個有效的 TraceId
是一個 16 字節(jié)的數(shù)組,且至少有一個非零字節(jié)。
SpanId
一個有效的 SpanId
是一個 8 字節(jié)的數(shù)組,且至少有一個非零字節(jié)。
TraceFlags
包含該 trace 的詳情。不像 TraceFlags
,TraceFlags
影響所有的 traces。當(dāng)前版本和定義的 Flags 只有 sampled 。
TraceState
攜帶特定 trace 標(biāo)識數(shù)據(jù),通過一個 KV 對數(shù)組進行標(biāo)識。TraceState
允許多個跟蹤系統(tǒng)參與同一個 Trace。完整定義請參考 W3C Trace Context specification 。
本 API 必須實現(xiàn)創(chuàng)建 SpanContext 的方法。這些方法應(yīng)當(dāng)是唯一的方法用于創(chuàng)建 SpanContext。這個功能必須在 API 中完全實現(xiàn),并且不應(yīng)當(dāng)可以被覆蓋。
但 SpanContext 并不是 Context,因而還需要做一層轉(zhuǎn)換。
private Context withSpanContext(SpanContext spanContext, Context context) { return context.with(Span.wrap(spanContext)); }
完整代碼如下:
/*** * @Description 通過已知的traceId和spanId,來構(gòu)造一個新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)); }
需要特別注意,依據(jù)當(dāng)前測試寫法的請求自身會產(chǎn)生一個新的 trace 信息。新構(gòu)造的 span 是依據(jù)傳入的參數(shù)進行構(gòu)造。
我們可以通過訪問鏈接來觀察結(jié)果
http://localhost:8080/customSpanByTraceIdAndSpanId?spanName=tSpan&traceId=24baeeddfbb35fceaf4c18e7cae58fe1&spanId=ff1955b4f0eacc4f
指標(biāo)(Metrics)
OpenTelemetry 還提供了 metric 相關(guān)操作的 API。
span 提供關(guān)于應(yīng)用程序的詳細信息,但生成的數(shù)據(jù)與系統(tǒng)上的負載成正比。相比之下,度量將單個度量組合成聚合,并生成作為系統(tǒng)負載函數(shù)的常量數(shù)據(jù)。聚合缺乏診斷低級問題所需的細節(jié),但是通過幫助識別趨勢和提供應(yīng)用程序運行時遙測來補充跨度。
度量 API 定義了各種工具。儀器記錄測量值,這些測量值由度量 SDK 聚合,并最終導(dǎo)出到進程外。儀器有同步和異步兩種。同步儀器記錄測量結(jié)果。異步儀器注冊回調(diào),每次采集調(diào)用一次,并記錄該時間點的測量值。下列儀器可供選擇:
LongCounter/DoubleCounter: 只記錄正數(shù)值,有同步和異步選項。用于計算事物,例如通過網(wǎng)絡(luò)發(fā)送的字節(jié)數(shù)。默認情況下,計數(shù)器測量被聚合為始終遞增的單調(diào)和。
LongUpDownCounter/DoubleUpDownCounter: 記錄正負值,有同步和異步選項。對于計算上升和下降的東西很有用,比如隊列的大小。默認情況下,向上向下計數(shù)器測量被聚合為非單調(diào)和。
LongGauge/DoubleGauge: 用異步回調(diào)測量瞬時值。用于記錄不能跨屬性合并的值,如 CPU 使用率百分比。默認情況下,量規(guī)測量被聚合為量規(guī)。
LongHistogram/DoubleHistogram(長直方圖/雙直方圖): 記錄對直方圖分布分析最有用的測量值。沒有異步選項可用。用于記錄 HTTP 服務(wù)器處理請求所花費的時間等。默認情況下,直方圖測量被聚合為顯式的桶直方圖。
獲取 Meter 對象
API定義了一個 Meter 接口。該接口由一組 instrument 構(gòu)造器,和一個使用原子方式批量獲取測量值的工具組成。Meter 可以通過 MeterProvider
的 getMeter(name)方法來創(chuàng)建一個新實例。 MeterProvider
通常被期望作為單例來使用。 其實現(xiàn)應(yīng)作為 MeterProvider
全局的唯一實現(xiàn)。通過 Meter 對象可以構(gòu)建不同類型的 metric。
@Bean public Meter meter() { return openTelemetry().getMeter(appName); }
這里跳過了 MeterProvider
的細節(jié),主要原因在于 OpenTelemetry Interface 提供了 MeterProvider
的默認 noop 實現(xiàn)。
default MeterProvider getMeterProvider() { return MeterProvider.noop(); }
構(gòu)建 gauge 類型的 metric
meter().gaugeBuilder("connections") .setDescription("當(dāng)前Socket.io連接數(shù)") .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
是一個回調(diào)函數(shù),是支持異步 API 并按需收集度量數(shù)據(jù)的附加工具,按間隔收集數(shù)據(jù),默認 1min
一次。
參考
springboot-opentelemetry-otlp-server
以上就是OpenTelemetry Java SDK 高級用法解析的詳細內(nèi)容,更多關(guān)于OpenTelemetry Java SDK的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Springboot @Cacheable 注解(指定緩存位置)
這篇文章主要介紹了詳解Springboot @Cacheable 注解(指定緩存位置),使用? @Cacheable ?注解就可以將運行結(jié)果緩存,以后查詢相同的數(shù)據(jù),直接從緩存中取,不需要調(diào)用方法,需要的朋友可以參考下2023-09-09Java Spring-IOC容器與Bean管理之基于注解的方式案例詳解
這篇文章主要介紹了Java Spring-IOC容器與Bean管理之基于注解的方式案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08