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

Spring Boot中WebClient的subscribe/onStatus方法日志缺失Trace ID

Spring Boot中WebClient的subscribe/onStatus日志缺失Trace ID问题

在Spring Boot应用中,WebClient调用的onStatus方法回调以及subscribe的回调内生成的日志无法显示Trace ID。已通过Micrometer配置让所有日志包含Trace ID,且WebClient附加的过滤器中日志能正常显示Trace ID,说明追踪配置功能正常,但上述特定位置日志仍缺失该ID。

环境信息

  • Spring Boot版本:3.3.0
  • Java版本:21

依赖配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>

WebClient配置

@Bean
public WebClient webclientBean(WebClient.Builder builder) throws SSLException {
    return builder
            .baseUrl(getBaseUrl())
            .exchangeStrategies(ExchangeStrategies
                    .builder()
                    // 以下过滤器中的日志正常,包含trace id 
                    .filter(WebClientFilter.logRequest())  
                    .filter(WebClientFilter.logResponse())  
                    .filter(WebClientFilter.handleError()) 
                    .clientConnector(new ReactorClientHttpConnector(WebClientFilter.httpClient()))
                    .build());
}

主类上下文传播配置

@PostConstruct
public void init() {
    Hooks.enableAutomaticContextPropagation();
}

问题代码(WebClient调用逻辑)

public Object webClientDispatchPost(Object input, String endpoint) {
    return webClient.post().uri(endpoint).body(input != null ? BodyInserters.fromValue(input) : BodyInserters.empty()).retrieve()
            .onStatus(HttpStatusCode::is2xxSuccessful, (r) -> {
                log.info("trace id does not logged here !!!");
                // do something 
            })
            .bodyToMono(Object.class)
            .subscribe(response -> {
                        log.info("trace id does not logged here !!!");
                    },
                    error -> {
                        log.info("trace id does not logged here !!!");
                    },
                    () -> log.info("trace id does not logged here !!!"));
}

已尝试的解决方法

  • 尝试在subscribe方法中访问MDC,但MDC为空(过滤器中可正常获取Trace ID)
  • 添加micrometer-tracing-bridge-otel依赖,无效果

解决方案

1. 避免手动调用subscribe(推荐)

将方法返回类型改为Mono<Object>,使用Reactor的doOnNext、doOnError、doOnComplete操作符替代subscribe的回调逻辑,由上层调用方(如Controller)负责订阅。这样Reactor上下文会自动传播,Trace ID可正常写入日志:

public Mono<Object> webClientDispatchPost(Object input, String endpoint) {
    return webClient.post()
            .uri(endpoint)
            .body(input != null ? BodyInserters.fromValue(input) : BodyInserters.empty())
            .retrieve()
            .onStatus(HttpStatusCode::is2xxSuccessful, (r) -> {
                log.info("trace id should appear here now!!!");
                // do something
                return Mono.empty();
            })
            .bodyToMono(Object.class)
            .doOnNext(response -> log.info("trace id should appear here now!!!"))
            .doOnError(error -> log.info("trace id should appear here now!!!"))
            .doOnComplete(() -> log.info("trace id should appear here now!!!"));
}

2. 若必须手动订阅,手动传播上下文

通过Mono.deferContextual获取当前Reactor上下文,在subscribe回调中打开上下文作用域,确保MDC能捕获Trace ID:

public void webClientDispatchPost(Object input, String endpoint) {
    Mono<Object> mono = webClient.post()
            .uri(endpoint)
            .body(input != null ? BodyInserters.fromValue(input) : BodyInserters.empty())
            .retrieve()
            .onStatus(HttpStatusCode::is2xxSuccessful, (r) -> {
                log.info("trace id should appear here now!!!");
                return Mono.empty();
            })
            .bodyToMono(Object.class);

    mono.deferContextual(ctx -> {
        mono.subscribe(
                response -> {
                    try (ContextView.Scope scope = ctx.openScope()) {
                        log.info("trace id should appear here now!!!");
                    }
                },
                error -> {
                    try (ContextView.Scope scope = ctx.openScope()) {
                        log.info("trace id should appear here now!!!");
                    }
                },
                () -> {
                    try (ContextView.Scope scope = ctx.openScope()) {
                        log.info("trace id should appear here now!!!");
                    }
                }
        );
        return Mono.empty();
    }).subscribe();
}

3. 验证日志格式配置

确保日志框架(如Logback)的格式配置中包含Trace ID变量,示例Logback配置:

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %X{traceId}%n</pattern>
    </encoder>
</appender>

内容的提问来源于stack exchange,提问作者Ibrahim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 20:59:53