Home > Software design >  Not able to pass Sleuth traceid and spanid to downstream services while making API calls using restt
Not able to pass Sleuth traceid and spanid to downstream services while making API calls using restt

Time:08-18

Currently, I am integrating Sleuth into our existing Microservices to enhance the logging mechanism. The MS is built with Spring Boot 2.5.6 and Spring Cloud 2020.0.6 (This has resolved to Spring Cloud Sleuth 3.0.6)

The challenge that I am trying to solve is while making outbound API calls, we are creating RestTemplate using new() keyword instead of managing RestTemplate as Bean. And unfortunately, we don't have the liberty to change the approach to use resttemplate as Bean in order for Sleuth and Brave instrumentation to work seamlessly (inorder to avoid introducing any breaking changes in the application) to propagate traceid to downstream logs(as mentioned in the official doc). Due to this, the traceids vary in the MS1 and MS2 logs, which makes the tracing difficult.

As I understand from the official doc, Brave registers interceptors to like LazyTraceClientHttpRequestInterceptor & TracingClientHttpRequestInterceptor to add headers like X-B3-Traceid, X-B3-SpanId to the outbound request, I tried to do something similar.

Here is the code for my custom Interceptor:

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
 
@Component
public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
    
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    
    private Tracer tracer;
    
    RequestResponseLoggingInterceptor(Tracer tracer){
        this.tracer = tracer;
    }
  
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException 
    {
        Span span = tracer.currentSpan();
        
        HttpHeaders headers = new HttpHeaders();
        headers.set("X-B3-Traceid", span.context().traceId());
        headers.set("X-B3-SpanId", span.context().spanId());
        headers.set("X-B3-ParentSpanId", span.context().parentId());
        request.getHeaders().addAll(headers);
        
        ClientHttpResponse response = execution.execute(request, body);
        return response;
    }
}

And In my controller class, I have autowired this interceptor:

@Autowired
private RequestResponseLoggingInterceptor interceptor;

and using this interceptor for the resttemplate call as below:

RestTemplate restTemplate = new RestTemplate();
        restTemplate.setInterceptors(Collections.singletonList(interceptor));
        ResponseEntity<ProductSale> responseEntity = restTemplate.getForEntity(
                "http://localhost:8000/product-service/pname/{pname}/cost/{cost}/qty/{qty}", ProductSale.class,
                uriVariables);
        ProductSale sales = responseEntity.getBody();

When I am comparing the MS1 and MS2 logs for this API call, the traceids are different.

  • In my understanding the MS2 SHOULD LOOK FOR X-B3 fields to generate
    the traceids for the log, but it's not using the traceid sent in the request header.
  • And also, in the usual setting, the span for the MS2 should be already injected in the headers inside MS1 only before
    making the call. But when I debug and check Trace id and SpanId in my interceptor they are same.

Can anyone help me what am I doing wrong here? And how can I achieve this traceid propagation to downstream service By Creating RestTemplate with New() keyword.

CodePudding user response:

I've not worked a lot with Sleuth but I have great suspicion over the creating of a new RestTemplate each time.

Try defining it as a bean with a singleton scope and then Autowire the RestTemplate.

CodePudding user response:

You need to inject TracingClientHttpRequestInterceptor bean to your rest template and it will take care of passing the tracing context.

  • Related