使用OpenTelemetry时MDC日志出现两个traceId问题排查
解决OpenTelemetry分布式日志中出现两个traceId的问题
你的日志里同时出现traceId和trace_id两个traceId字段,核心原因有两点:
- Tracer实例创建错误:你在每个请求的Filter里都重新构建
OpenTelemetrySdk和SdkTracerProvider,这会生成独立于全局链路上下文的新traceId,和系统中已有的OpenTelemetry链路不统一。 - 重复注入traceId:你手动往MDC里放入了
traceId/spanId,同时OpenTelemetry的日志集成(比如logback/log4j的OpenTelemetry Appender)默认会自动往MDC注入trace_id/span_id字段,导致重复。
下面是具体的修复步骤:
步骤1:修复Tracer的获取方式
绝对不能在Filter里每次请求都创建新的OpenTelemetry实例,必须全局初始化一次并复用:
全局初始化OpenTelemetry(配置类示例)
@Configuration public class OpenTelemetryConfig { @Bean public OpenTelemetry openTelemetry() { SdkTracerProvider tracerProvider = SdkTracerProvider.builder() // 这里可添加采样器、链路导出器(如Jaeger)等配置 .build(); // 注册为全局实例,后续全系统复用 return OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) .buildAndRegisterGlobal(); } @Bean public Tracer tracer(OpenTelemetry openTelemetry) { return openTelemetry.getTracer("oTelTracer"); } }
修改TracingFilter,注入全局Tracer
public class TracingFilter implements Filter { private final Tracer tracer; // 通过构造器注入全局Tracer public TracingFilter(Tracer tracer) { this.tracer = tracer; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 继承当前上下文的父Span,确保链路traceId统一 Span restSpan = tracer.spanBuilder("restSpan") .setParent(Context.current()) .startSpan(); try (Scope scope = restSpan.makeCurrent()) { // 这里选择和OpenTelemetry默认key一致,避免重复字段 MDC.put("trace_id", restSpan.getSpanContext().getTraceId()); MDC.put("span_id", restSpan.getSpanContext().getSpanId()); chain.doFilter(request, response); } finally { // 必须结束Span,避免内存泄漏 restSpan.end(); // 清理MDC,防止线程复用导致脏数据 MDC.remove("trace_id"); MDC.remove("span_id"); } } }
步骤2:消除重复的traceId字段
有两种可选方案:
方案A:统一MDC的key和OpenTelemetry默认一致
OpenTelemetry日志集成默认注入的字段key是trace_id/span_id,所以手动放入MDC时也用这些key,这样就不会出现两个不同的traceId字段。
方案B:关闭OpenTelemetry自动日志注入
如果你想保留自定义的traceId/spanId字段,可以关闭OpenTelemetry的自动注入功能。以logback为例,修改配置:
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender"> <!-- 关闭自动注入trace_id等字段 --> <disableEventAttributes>true</disableEventAttributes> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg [traceId=%X{traceId}, spanId=%X{spanId}]%n</pattern> </encoder> </appender>
内容的提问来源于stack exchange,提问作者Abhishek Pandit
相关产品推荐
相关产品推荐

