Spring Boot 3中WebClient Wiretap日志缺失TraceId和SpanId问题
解决WebClient请求响应日志缺失TraceId/SpanId的问题
1. 确认依赖配置
确保项目依赖包含链路追踪所需组件(以Maven为例):
<!-- Spring Boot Actuator(Micrometer基础依赖) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- Micrometer Tracing 桥接Brave(主流适配方案) --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-brave</artifactId> </dependency> <!-- Brave日志MDC集成,实现上下文传递 --> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-context-log4j2</artifactId> </dependency>
2. 修改WebClient配置,绑定追踪上下文
调整你的WebClient Builder,添加追踪过滤器,确保TraceId/SpanId能同步到MDC并传递到下游请求:
@Bean("tokenExchangeNoTanRequired") @Scope(SCOPE_PROTOTYPE) public WebClient.Builder tokenExchangeNoTanRequiredWebClientBuilder( @Qualifier("exchangeBearerTokenNoTanRequiredFilter") ExchangeFilterFunction filter, ClientRequestObservationConvention observationConvention) throws SSLException { return WebClient.builder() .clientConnector(getClientConnector()) .filter(filter) // 绑定追踪上下文到MDC,供日志输出 .filter(ExchangeFilterFunction.ofRequestProcessor(clientRequest -> Mono.deferContextual(contextView -> { TraceContext traceContext = contextView.getOrDefault(TraceContext.class, null); if (traceContext != null) { MDC.put("traceId", traceContext.traceId()); MDC.put("spanId", traceContext.spanId()); } return Mono.just(clientRequest); }) )) // 请求结束后清理MDC,避免上下文污染 .filter(ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { MDC.remove("traceId"); MDC.remove("spanId"); return Mono.just(clientResponse); })) .observationConvention(observationConvention); } private HttpClient getHttpClient() throws SSLException { SslContext sslContext = SslContextBuilder .forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) .build(); return HttpClient .create() .secure(t -> t.sslContext(sslContext)) .wiretap(this.getClass().getCanonicalName(), LogLevel.INFO, AdvancedByteBufFormat.TEXTUAL) .responseTimeout(Duration.of(this.responseTimeout, MILLIS)) // 启用Reactor上下文捕获,保证异步场景下追踪信息不丢失 .contextCapture(true); }
3. 配置日志格式,输出TraceId/SpanId
以Logback为例,修改logback-spring.xml,在日志模板中加入追踪字段,确保Elasticsearch能解析到这些信息:
<!-- 控制台日志格式 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId:%X{traceId:-} spanId:%X{spanId:-} - %msg%n</pattern> </encoder> </appender> <!-- Logstash输出到Elasticsearch的格式 --> <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <destination>your-es-logstash-address:port</destination> <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"> <providers> <timestamp> <timeZone>UTC</timeZone> </timestamp> <pattern> <pattern> { "level": "%level", "logger": "%logger", "traceId": "%X{traceId:-}", "spanId": "%X{spanId:-}", "message": "%message" } </pattern> </pattern> </providers> </encoder> </appender>
4. 开启Spring Boot追踪配置
在application.yml中添加以下配置,启用全量采样和链路传播:
management: tracing: sampling: probability: 1.0 # 测试阶段设为100%采样,生产可调整 propagation: type: B3 # 兼容Zipkin、Elastic APM的传播协议 logging: level: reactor.netty.http.client: INFO # 确保WebClient请求日志正常输出 io.micrometer.tracing: DEBUG # 可选,用于排查追踪上下文问题
核心逻辑说明
- Reactor异步场景下,TraceId/SpanId存储在Reactor Context中,需通过
deferContextual同步到MDC,才能被日志框架捕获。 - WebClient的
observationConvention确保追踪信息被Micrometer正确记录,实现链路追踪与日志的关联。 - 若使用OpenTelemetry替代Brave,只需替换依赖为
micrometer-tracing-bridge-otel,配置逻辑基本一致。
内容的提问来源于stack exchange,提问作者Mehrdad Bozorgmehr
相关产品推荐
相关产品推荐

