OpenTelemetry

OpenTelemetry 是一个开源项目,旨在通过统一的工具集、API和SDK,简化在多样化的技术栈中集成可观测性功能,确保一致地收集、处理及导出应用性能数据。

如何通过 OpenTelemetry 过滤指定 Span


OpenTelemetry Samplers(采样器)用于控制哪些链路数据会被采集和发送,因此可以使用 Samplers 来过滤指定的Span。本文介绍如何使用 OpenTelemetry Samplers 过滤 Java 应用或者 Node.js 应用中的指定 Span。

Java

方法一:无代码侵入方式(基于 Java Agent Extension 实现)

该方法需要对 OpenTelemetry Java Agent 的功能进行扩展,在扩展程序中编写自定义的 OpenTelemetry Sampler,并在 Sampler 中定义 Span 过滤规则。最后在应用启动时同时加载 OpenTelemetry Java Agent 和扩展,实现无侵入的 Span 过滤。

新建 Java Agent 扩展项目

在 pom.xml 中添加以下依赖,并替换 <font style="color:rgb(24, 24, 24);">${OPENTELEMETRY_VERSION}</font>,确保与您使用的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>${OPENTELEMETRY_VERSION}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-trace</artifactId>
<version>${OPENTELEMETRY_VERSION}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
<version>${OPENTELEMETRY_VERSION}</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-semconv</artifactId>
<version>${OPENTELEMETRY_VERSION}-alpha</version>
</dependency>

Java Agent 扩展项目中实现采样处理类

  1. 新建 SpanFilterSampler 类文件(类名可自定义)

SpanFilterSampler 类需要实现io.opentelemetry.sdk.trace.samplers.Sampler接口,并实现 shouldSample 方法和 getDescription 方法。

  • shouldSample 方法:可以在方法中自定义过滤条件。对于要过滤的 Span,返回 SamplingResult.create(SamplingDecision.DROP),需要保留并继续上报的 Span 则返回 SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE)
  • getDescription 方法:返回自定义 Sampler 的名称。
  1. 示例代码

在下面示例代码中,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可以替换为自定义的名称
}
}

实现采样提供者类

  1. 新建 SpanFilterSamplerProvider 类文件(类名可自定义)

SpanFilterSamplerProvider 类需要实现<font style="color:rgba(0, 0, 0, 0.85);">io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider</font>接口,实现 createSampler 方法和 getName 方法。

  • createSampler 方法:创建并返回您自定义的 Sampler 实例。
  • getName 方法:获取自定义的 Sampler 名称,OpenTelemetry Java Agent 通过该名称找到这个 Sampler。
  1. 示例代码
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 pacakage

启动应用时加载 OpenTelemetry Java Agent 扩展

有两种加载方式:

  1. VM 参数方式加载
  • ${your-sampler-name}替换为您自定义的 Sampler 名称,也就是 getName 方法的返回值。
-Dotel.traces.sampler=${your-sampler-name}
  • 示例
java -javaagent:path/to/opentelemetry-javaagent.jar \
-Dotel.javaagent.extensions=path/to/opentelemetry-java-agent-extension.jar \
-Dotel.traces.sampler=${your-sampler-name} \
-Dotel.exporter.otlp.endpoint=${endpoint} \
-jar yourapp.jar
  1. 环境变量方式加载
  • ${your-sampler-name}替换为您自定义的 Sampler 名称,也就是 getName 方法的返回值。
export OTEL_TRACES_SAMPLER="${your-sampler-name}"
  • 示例
export OTEL_JAVAAGENT_EXTENSIONS="path/to/opentelemetry-java-agent-extension.jar"
export OTEL_TRACES_SAMPLER="${your-sampler-name}"
export OTEL_EXPORTER_OTLP_ENDPOINT="${endpoint}"
java -javaagent:path/to/opentelemetry-javaagent.jar \
-jar yourapp.jar

方法二:使用 OpenTelemetry Java SDK

前提条件

已经使用 OpenTelemetry Java SDK 为应用程序手动埋点。

在业务代码中创建采样处理类

  1. SpanFilterSampler 类需要实现io.opentelemetry.sdk.trace.samplers.Sampler接口,并实现 shouldSample 方法和 getDescription 方法。
  • shouldSample 方法:可以在方法中自定义过滤条件。对于要过滤的 Span,返回 SamplingResult.create(SamplingDecision.DROP),需要保留并继续上报的 Span 则返回 SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE)
  • getDescription 方法:返回自定义 Sampler 的名称。
  1. 示例代码

在下面示例代码中,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可以替换为自定义的名称
}
}

在创建 SdkTracerProvider 实例时设置自定义 Sampler

在创建 SdkTracerProvider 实例时,调用 setSampler(new SpanFilterSampler()),即可完成配置。

...
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
.setSampler(new MySampler()) // 添加这一行
.addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder()
.setEndpoint("${endpoint}")
.build()).build())
.setResource(resource)
.build();
...

启动应用

Node.js

实现自定义 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}",
}),
});
...

observability.cn Authors 2024 | Documentation Distributed under CC-BY-4.0
Copyright © 2017-2024, Alibaba. All rights reserved. Alibaba has registered trademarks and uses trademarks.
浙ICP备2021005855号-32