本文介紹對於Java和Node.js應用如何使用OpenTelemetry過濾指定的Span。
Java
OpenTelemetry Samplers可用來過濾Span。如果不希望修改業務代碼,或者已經使用了OpenTelemetry Java Agent自動上報應用資料,那麼可以參考方法一對OpenTelemetry Java Agent的功能進行擴充,編寫自訂的OpenTelemetry Sampler,通過在Sampler中定義過濾規則,實現無侵入的Span過濾。如果您已經使用了OpenTelemetry Java SDK手動上報應用資料,可以參考方法二在業務代碼中編寫自訂Sampler。
方法一:使用OpenTelemetry Java Agent Extension(自動埋點 / 無侵入式)
前提條件
已經使用OpenTelemetry Java Agent為應用程式自動埋點。具體操作,請參見通過OpenTelemetry上報Java應用資料。
建立專案。
建立一個空白的Maven專案,用於建立和構建Agent Extension,對Agent的功能進行擴充。
在pom.xml中添加依賴。
重要為保證依賴互相相容,請將各項opentelemetry依賴設定為同一版本,且與您使用的OpenTelemetry Java Agent版本保持一致。
<dependency> <groupId>com.google.auto.service</groupId> <artifactId>auto-service</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>io.opentelemetry.javaagent</groupId> <artifactId>opentelemetry-javaagent</artifactId> <version>1.28.0</version> <!--這裡要設定為compile的--> <scope>compile</scope> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-trace</artifactId> <version>1.28.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId> <version>1.28.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-semconv</artifactId> <version>1.28.0-alpha</version> </dependency>
建立SpanFilterSampler類(類名可自訂)。
需要實現io.opentelemetry.sdk.trace.samplers.Sampler介面,並實現shouldSample方法和getDescription方法。
shouldSample方法
可以在方法中自訂過濾條件。對於要過濾的Span,返回
SamplingResult.create(SamplingDecision.DROP)
,需要保留並繼續上報的Span則返回SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE)
。
getDescription方法
返回自訂Sampler的名稱。
例如,下面範例程式碼中,SpanFilterSampler會過濾名稱為
spanName1
或spanName2
的Span,以及attributes.http.target為/api/checkHealth
或/health/checks
的所有Span。package org.example; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.sdk.trace.samplers.SamplingDecision; import io.opentelemetry.sdk.trace.samplers.SamplingResult; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.*; public class SpanFilterSampler implements Sampler { /* * 過濾Span名稱在EXCLUDED_SPAN_NAMES中的所有Span */ private static List<String> EXCLUDED_SPAN_NAMES = Collections.unmodifiableList( Arrays.asList("spanName1", "spanName2") ); /* * 過濾attributes.http.target在EXCLUDED_HTTP_REQUEST_TARGETS中的所有Span */ private static List<String> EXCLUDED_HTTP_REQUEST_TARGETS = Collections.unmodifiableList( Arrays.asList("/api/checkHealth", "/health/checks") ); @Override public SamplingResult shouldSample(Context parentContext, String traceId, String name, SpanKind spanKind, Attributes attributes, List<LinkData> parentLinks) { String httpTarget = attributes.get(SemanticAttributes.HTTP_TARGET) != null ? attributes.get(SemanticAttributes.HTTP_TARGET) : ""; if (EXCLUDED_SPAN_NAMES.contains(name) || EXCLUDED_HTTP_REQUEST_TARGETS.contains(httpTarget)) { // 根據條件進行過濾 return SamplingResult.create(SamplingDecision.DROP); } else { return SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE); } } @Override public String getDescription() { return "SpanFilterSampler"; // SpanFilterSampler可以替換為自訂的名稱 } }
建立SpanFilterSamplerProvider類(類名可自訂)。
需要實現io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider介面,實現createSampler方法和getName方法。
createSampler方法
建立並返回您自訂的Sampler執行個體。
getName方法
擷取自訂的Sampler名稱,OpenTelemetry Java Agent通過該名稱找到這個Sampler。
package org.example; import com.google.auto.service.AutoService; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; import io.opentelemetry.sdk.trace.samplers.Sampler; @AutoService(ConfigurableSamplerProvider.class) public class SpanFilterSamplerProvider implements ConfigurableSamplerProvider { @Override public Sampler createSampler(ConfigProperties configProperties) { return new SpanFilterSampler(); } @Override public String getName() { return "SpanFilterSampler"; // SpanFilterSampler可以替換為自訂的名稱 } }
構建.
將程式打包成JAR包,構建後儲存在target目錄下。
mvn clean package
啟動應用時載入OpenTelemetry Java Agent擴充。
方法一:在原有VM參數上添加otel.traces.sampler參數
-Dotel.traces.sampler=<your-sampler-name> // 將<your-sampler-name>替換為您自訂的Sampler名稱,也就是getName方法的傳回值
方法二:設定OTEL_TRACES_SAMPLER環境變數。
export OTEL_TRACES_SAMPLER="<your-sampler-name>" // 將<your-sampler-name>替換為您自訂的Sampler名稱,也就是getName方法的傳回值
方法二:使用OpenTelemetry Java SDK(手動埋點)
前提條件
已經使用OpenTelemetry Java SDK為應用程式手動埋點。具體操作,請參見通過OpenTelemetry上報Java應用資料。
在您的業務代碼中建立自訂Sampler類。
需要實現io.opentelemetry.sdk.trace.samplers.Sampler介面,並實現shouldSample方法和getDescription方法。
shouldSample方法
可以在方法中自訂過濾條件。對於要過濾的Span,返回
SamplingResult.create(SamplingDecision.DROP)
,需要保留並繼續上報的Span則返回SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE)
。
getDescription方法
返回自訂Sampler的名稱。
例如,下面範例程式碼中,SpanFilterSampler會過濾名稱為
spanName1
或spanName2
的Span,以及attributes.http.target為/api/checkHealth
或/health/check
的所有Span。package org.example; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.samplers.Sampler; import io.opentelemetry.sdk.trace.samplers.SamplingDecision; import io.opentelemetry.sdk.trace.samplers.SamplingResult; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.*; public class SpanFilterSampler implements Sampler { /* * 過濾Span名稱在EXCLUDED_SPAN_NAMES中的所有Span */ private static List<String> EXCLUDED_SPAN_NAMES = Collections.unmodifiableList( Arrays.asList("spanName1", "spanName2") ); /* * 過濾attributes.http.target在EXCLUDED_HTTP_REQUEST_TARGETS中的所有Span */ private static List<String> EXCLUDED_HTTP_REQUEST_TARGETS = Collections.unmodifiableList( Arrays.asList("/api/checkHealth", "/health/checks") ); @Override public SamplingResult shouldSample(Context parentContext, String traceId, String name, SpanKind spanKind, Attributes attributes, List<LinkData> parentLinks) { String httpTarget = attributes.get(SemanticAttributes.HTTP_TARGET) != null ? attributes.get(SemanticAttributes.HTTP_TARGET) : ""; if (EXCLUDED_SPAN_NAMES.contains(name) || EXCLUDED_HTTP_REQUEST_TARGETS.contains(httpTarget)) { // 根據條件進行過濾 return SamplingResult.create(SamplingDecision.DROP); } else { return SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE); } } @Override public String getDescription() { return "SpanFilterSampler"; // SpanFilterSampler可以替換為自訂的名稱 } }
在建立SdkTracerProvider執行個體時設定自訂Sampler。
在建立SdkTracerProvider執行個體時,調用
setSampler(new SpanFilterSampler())
,即可完成配置。... SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .setSampler(new MySampler()) // 添加這一行 .addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder() .setEndpoint("<endpoint>") .addHeader("Authentication", "<token>") .build()).build()) .setResource(resource) .build(); ...
啟動應用。
Node.js
樣本Demo:opentelemetry-nodejs-demo。
前提條件
已經使用OpenTelemetry JavaScript API為應用程式埋點。具體操作,請參見通過OpenTelemetry上報Node.js應用資料。
方法一:在Span建立階段過濾
建立HttpInstrumentation時設定ignoreIncomingRequestHook。
建立HttpInstrumentation時可以設定HttpInstrumentationConfig參數,參數包括ignoreIncomingRequestHook,該參數允許使用者傳入一個自訂方法,在請求被處理前判斷是否需要建立埋點。需要注意,此方法的作用只是不建立Span,而不會影響該請求的執行。
例如,下面代碼中展示了如何過濾
request.url=/api/checkHealth
的請求:... // 要被替換的內容 // registerInstrumentations({ // tracerProvider: provider, // instrumentations: [new HttpInstrumentation(), ExpressInstrumentation], // }); const httpInstrumentation = new HttpInstrumentation({ // 添加一個自訂的ignoreIncomingRequestHook ignoreIncomingRequestHook: (request) => { //忽略request.url為/api/checkHealth的請求 if (request.url === '/api/checkHealth') { return true; } return false; }, }); registerInstrumentations({ tracerProvider: provider, instrumentations: [httpInstrumentation, ExpressInstrumentation], }); ...
啟動應用。
方法二:在Span上報階段過濾
建立自訂Sampler類。
建立一個實現了Sampler介面的自訂Sampler類。該介面定義了是否採樣的規則。例如:
const opentelemetry = require('@opentelemetry/api'); class SpanFilterSampler { shouldSample(spanContext, parentContext) { // 在此處實現您的自訂採樣邏輯 } }
建立NodeTracerProvider執行個體時設定自訂Sampler。
... const provider = new NodeTracerProvider({ sampler: new SpanFilterSampler(), // 添加這一行代碼,設定自訂Sampler resource: new Resource({ [SemanticResourceAttributes.HOST_NAME]: require("os").hostname(), // your host name [SemanticResourceAttributes.SERVICE_NAME]: "<your-service-name>", }), }); ...