如何配置Java版OpenTelemetry将MDC值标记到Span?
将MDC中的客户ID自动添加到所有OTEL Span的解决方案
1. 通过配置自动提取MDC值到Span标签
可以直接通过OTEL配置实现,无需编写代码。OTEL Java SDK支持通过otel.instrumentation.common.mdc.export配置键值对,指定MDC键与对应Span标签名的映射。
比如要把MDC中customerId的值映射为Span的customer.id标签,只需添加以下配置:
otel.instrumentation.common.mdc.export=customerId:customer.id
该配置会让所有自动仪器化生成的Span(如Servlet请求、JDBC调用、HTTP客户端请求等)自动从当前MDC上下文提取指定键的值,添加为Span属性。如果需要映射多个键,用逗号分隔即可:
otel.instrumentation.common.mdc.export=customerId:customer.id,requestId:trace.request.id
2. 通过Servlet过滤器手动注入标签
如果配置方式无法满足需求(比如需要额外逻辑处理),可以在请求入口处添加Servlet过滤器,将MDC中的客户ID注入到OTEL上下文,确保后续所有Span自动继承该标签。
示例代码如下:
import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; import org.slf4j.MDC; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter(urlPatterns = "/*") public class CustomerIdOtelFilter implements Filter { private static final String MDC_CUSTOMER_KEY = "customerId"; private static final String SPAN_CUSTOMER_ATTR = "customer.id"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String customerId = MDC.get(MDC_CUSTOMER_KEY); if (customerId != null) { // 获取当前Span,若不存在则创建根Span(可选) Span currentSpan = Span.current(); currentSpan.setAttribute(SPAN_CUSTOMER_ATTR, customerId); // 将更新后的Span上下文设为当前,后续所有操作生成的Span都会继承该属性 Context updatedContext = Context.current().with(currentSpan); try (Scope ignored = updatedContext.makeCurrent()) { chain.doFilter(request, response); } } else { chain.doFilter(request, response); } } }
注意:需确保过滤器在OTEL自带过滤器之前执行,避免无法捕获初始Span。
3. 自定义SpanProcessor全局注入标签
另一种全局方案是实现OTEL的SpanProcessor,在每个Span启动时自动从MDC提取值并添加为属性。这种方式不受请求入口限制,适用于所有生成Span的场景(包括异步任务)。
自定义SpanProcessor实现
import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.SpanProcessor; import org.slf4j.MDC; public class MdcCustomerIdSpanProcessor implements SpanProcessor { private static final String MDC_CUSTOMER_KEY = "customerId"; private static final String SPAN_CUSTOMER_ATTR = "customer.id"; @Override public void onStart(Context parentContext, ReadWriteSpan span) { String customerId = MDC.get(MDC_CUSTOMER_KEY); if (customerId != null) { span.setAttribute(SPAN_CUSTOMER_ATTR, customerId); } } @Override public boolean isStartRequired() { return true; } @Override public void onEnd(ReadWriteSpan span) {} @Override public boolean isEndRequired() { return false; } }
注册SpanProcessor到OTEL TracerProvider
在OTEL初始化阶段,将自定义处理器添加到TracerProvider:
import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; // 初始化TracerProvider时添加处理器 SdkTracerProviderBuilder tracerBuilder = SdkTracerProvider.builder(); tracerBuilder.addSpanProcessor(new MdcCustomerIdSpanProcessor()); // 其他配置... SdkTracerProvider tracerProvider = tracerBuilder.build();
内容的提问来源于stack exchange,提问作者James Courtney
相关产品推荐
相关产品推荐

