本文介绍对于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>", }), }); ...