Issue
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.
Solution
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.
Answered By - Shayan Daneshvar
Answer Checked By - David Goodson (JavaFixing Volunteer)