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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:10:57