You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用OpenTelemetry时MDC日志出现两个traceId问题排查

解决OpenTelemetry分布式日志中出现两个traceId的问题

你的日志里同时出现traceId和trace_id两个traceId字段,核心原因有两点:

  1. Tracer实例创建错误:你在每个请求的Filter里都重新构建OpenTelemetrySdk和SdkTracerProvider,这会生成独立于全局链路上下文的新traceId,和系统中已有的OpenTelemetry链路不统一。
  2. 重复注入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:02:04