Home > Mobile >  Spring Cloud Sleuth: tracer.getCurrentSpan() is NULL
Spring Cloud Sleuth: tracer.getCurrentSpan() is NULL

Time:10-25

Using Spring Cloud Sleuth version 3.1.4 and I want to insert the trace ID in the HTTP response headers. I created a bean of type GlobalFilter and trying to retrieve the trace ID string as follows

@Configuration
public class ResponseFilter {

    @Autowired
    Tracer tracer;
    
    @Autowired
    FilterUtils filterUtils;
    
    final Logger logger =LoggerFactory.getLogger(ResponseFilter.class);
    
    @Bean
    public GlobalFilter postGlobalFilter() {
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                  
                  String traceId = tracer.currentSpan().context().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());
              }));
        };
    }
}

I am getting tracer.currentSpan() as NULL which is why it gives NPE for the above code.

CodePudding user response:

The problem is that the currentSpan will expire in the then() method and when you want to get it, it simply doesn't exist. Hence, you should take it sooner.

Here's a solution where you can get the current span and retrieve the traceId:

    @Bean
    public GlobalFilter postGlobalFilter() {
        return (exchange, chain) -> {
            final String traceId = Optional.ofNullable(tracer.currentSpan())
                    .map(Span::context)
                    .map(TraceContext::traceIdString)
                    .orElse("null");

            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                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());
            }));
        };
    }

Note that I have used Java's Optional to deal with null values. You can use Mono.just(...).map(...).switchIfEmpty(...) as well.

Here's a cleaner way you could configure Spring Cloud Gateway's filter:

@Slf4j
@Configuration
@Profile({"dev","stage"})
@RequiredArgsConstructor
public class ResponseFilter implements GlobalFilter {
    private final Tracer tracer;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        final String traceId = Optional.ofNullable(tracer.currentSpan())
                .map(Span::context)
                .map(TraceContext::traceIdString)
                .orElse("null");

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.debug("Adding the correlation id to the outbound headers. {}", traceId);
            exchange.getResponse().getHeaders().add(FilterUtils.CORRELATION_ID, traceId);
            log.debug("Completing outgoing request for {}.",
                    exchange.getRequest().getURI());
        }));
    }

}

Keep in mind that exposing traceId in the http response of the gateway is usually not a good idea (according to Spring Cloud Sleuth Documentation), unless you are in dev or stage and you want to use it for debugging purposes.

  • Related