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

Spring Cloud Sleuth 3.1.4:tracer.currentSpan()返回NULL引发NPE问题求助

解决Spring Cloud Sleuth 3.1.4中tracer.currentSpan()返回NULL的问题

问题原因

在WebFlux的异步处理流程中,你在chain.filter(exchange).then()的回调里调用tracer.currentSpan()时,原请求的Span已经结束并被关闭。tracer.currentSpan()依赖当前线程的上下文,而异步回调的线程上下文已丢失Span信息,因此返回NULL引发空指针异常。


方案一:提前保存Span上下文

在调用chain.filter(exchange)之前(此时Span仍处于活跃状态)获取并保存Span上下文,后续在异步回调中直接使用该上下文:

@Configuration
public class ResponseFilter {

    @Autowired
    Tracer tracer;
    
    @Autowired
    FilterUtils filterUtils;
    
    final Logger logger = LoggerFactory.getLogger(ResponseFilter.class);
    
    @Bean
    public GlobalFilter postGlobalFilter() {
        return (exchange, chain) -> {
            // 提前获取活跃的Span上下文,避免回调中丢失
            SpanContext spanContext = tracer.currentSpan() != null ? tracer.currentSpan().context() : null;
            
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if (spanContext != null) {
                    String traceId = spanContext.traceIdString();
                    logger.debug("Adding the correlation id to the outbound headers. {}", traceId);
                    exchange.getResponse().getHeaders().add(FilterUtils.CORRELATION_ID, traceId);
                    logger.debug("Completing outgoing request for {}.", exchange.getRequest().getURI());
                } else {
                    logger.debug("No active span found for outgoing request {}", exchange.getRequest().getURI());
                }
            }));
        };
    }
}

方案二:通过Reactor上下文获取Trace信息

Spring Cloud Sleuth会将Trace上下文存入Reactor的Context中,直接从上下文取出Trace信息,更符合响应式编程模型:

@Configuration
public class ResponseFilter {

    @Autowired
    FilterUtils filterUtils;
    
    final Logger logger = LoggerFactory.getLogger(ResponseFilter.class);
    
    @Bean
    public GlobalFilter postGlobalFilter() {
        return (exchange, chain) -> {
            return chain.filter(exchange)
                    .then(Mono.deferContextual(contextView -> {
                        // 从Reactor上下文取出TraceContext
                        TraceContext traceContext = contextView.getOrDefault(TraceContext.class, null);
                        if (traceContext != null) {
                            String traceId = traceContext.traceIdString();
                            logger.debug("Adding the correlation id to the outbound headers. {}", traceId);
                            exchange.getResponse().getHeaders().add(FilterUtils.CORRELATION_ID, traceId);
                            logger.debug("Completing outgoing request for {}.", exchange.getRequest().getURI());
                        } else {
                            logger.debug("No trace context found in reactor context for outgoing request {}", exchange.getRequest().getURI());
                        }
                        return Mono.empty();
                    }));
        };
    }
}

注意事项

  • 两种方案都加入了空值判断,避免无Span场景(如健康检查接口)再次抛出NPE。
  • 方案二更适合复杂的异步响应式场景,能保证Trace信息在Reactor流中正确传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 08:05:27