Spring Cloud Sleuth迁移Micrometer Tracing后Gateway无Trace_ID问题
你的核心问题是Gateway中Micrometer Tracing与原生OpenTelemetry依赖冲突,导致追踪上下文未正确初始化,同时WebFlux场景下Reactor Context与MDC的自动同步未生效,进而出现日志无traceId/spanId、Span.current()返回全0的情况。以下是针对性解决步骤:
1. 清理Gateway的依赖冲突
你在Gateway中同时引入了原生OpenTelemetry API/SDK和Micrometer Tracing Bridge OTel,两套追踪上下文机制共存会互相干扰。Micrometer Tracing Bridge OTel已封装必要的OpenTelemetry依赖,无需手动引入原生OTel包。
修改Gateway的依赖配置,删除以下内容:
implementation(group: 'io.opentelemetry', name: 'opentelemetry-api') implementation(group: 'io.opentelemetry', name: 'opentelemetry-sdk') implementation(group: 'io.opentelemetry', name: 'opentelemetry-exporter-logging') implementation(group: 'io.opentelemetry', name: 'opentelemetry-semconv', version: '1.30.1-alpha')
仅保留common-tracing依赖即可,它已通过micrometer-tracing-bridge-otel提供桥接能力。
2. 确保WebFlux下Reactor Context与MDC自动同步
Spring Boot 3.x + Micrometer Tracing在WebFlux场景下,会自动通过TracingWebFilter初始化追踪Span,并通过ReactorContextMdcAutoConfiguration将Reactor Context中的追踪信息同步到MDC。无需手动设置MDC,只需确保依赖完整:
在common-tracing中确认包含WebFlux相关自动配置依赖(若Gateway为WebFlux项目,Spring Boot会自动引入,自定义库可显式添加):
api(group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '3.1.x') // 替换为你的Spring Boot版本
3. 调整自定义GlobalFilter的执行顺序
你的TraceContextGlobalFilter和TraceContextLoggingFilter可能在Micrometer的TracingWebFilter之前执行,此时追踪上下文尚未初始化,无法获取有效Span。
修改自定义Filter的执行顺序,确保在TracingWebFilter之后运行:
import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; // TracingWebFilter默认顺序为Ordered.HIGHEST_PRECEDENCE + 10,自定义Filter设为其后的顺序 @Order(Ordered.HIGHEST_PRECEDENCE + 20) public class TraceContextGlobalFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { // 此时已可通过Micrometer Tracer获取有效Span上下文 return chain.filter(exchange); } }
4. 正确获取Span上下文(避免直接调用OTel原生API)
WebFlux场景下,Span存储在Reactor Context中,不能直接在主线程调用Span.current()(OTel原生API),应使用Micrometer Tracing的Tracer API,并在Reactor序列中获取:
import io.micrometer.tracing.Tracer; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @Component public class SomeGatewayService { private final Tracer tracer; public SomeGatewayService(Tracer tracer) { this.tracer = tracer; } public Mono<Void> handleRequest() { return Mono.deferContextual(contextView -> { var currentSpan = tracer.currentSpan(); if (currentSpan != null) { String traceId = currentSpan.context().traceId(); String spanId = currentSpan.context().spanId(); // 业务逻辑处理 } return Mono.empty(); }); } }
5. 验证日志格式与MDC键名
你的日志格式中%X{traceId:-}和%X{spanId:-}与Micrometer Tracing默认MDC键名一致,无需修改。建议移除opentelemetry-logback-mdc依赖,改用Micrometer原生的MDC同步机制,避免版本兼容问题:
// 移除OTel的MDC依赖 // runtimeOnly("io.opentelemetry.instrumentation:opentelemetry-logback-mdc-1.0:2.0.0-alpha")
6. 确认配置生效
检查Gateway的application.yml配置,确保追踪配置正确加载:
management: tracing: enabled: true sampling: probability: 1.0 # 确保100%采样 propagation: type: tracecontext # 默认值,确保traceparent头正确解析传递
完成以上步骤后,Gateway的日志会自动输出traceId/spanId,Tracer.currentSpan()也能获取有效上下文,异常处理等场景会被自动覆盖,无需手动埋点。
内容的提问来源于stack exchange,提问作者wikenah

