Search code examples
javaspring-bootmicrometermicrometer-tracing

How to instrument Apache HttpClient using micrometer tracing


I'm using spring micrometer for tracing purpose and tried configuring the HttpClient the following way.

MicrometerHttpClientInterceptor interceptorProvider = new MicrometerHttpClientInterceptor(new SimpleMeterRegistry(),
        x -> x.getRequestLine().getUri(),
        Tags.empty(),
        false);


return HttpClients.custom()
        .addInterceptorFirst(interceptorProvider.getRequestInterceptor())
        .addInterceptorLast(interceptorProvider.getResponseInterceptor())
        .build();

But the http client is not adding tracing headers to any of the HTTP requests.

Is there anything equivalent to Brave's instrumentation or any support for interceptors from Micrometer?


Solution

  • EDITED ANSWER:

    After the initial solution, I later figured out the previous solution (below) creates a new observation leading to generation of new spanIds. This is undesired behavior.

    Hence I went with the interceptor approach:

    Interceptor:

    import io.micrometer.tracing.Tracer;
    import io.micrometer.tracing.propagation.Propagator;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HttpException;
    import org.apache.http.HttpMessage;
    import org.apache.http.HttpRequest;
    import org.apache.http.HttpRequestInterceptor;
    import org.apache.http.protocol.HttpContext;
    
    import java.io.IOException;
    import java.util.Arrays;
    
    
    @Slf4j
    public class TracingInterceptorHC4 implements HttpRequestInterceptor {
    
        private final Tracer tracer;
        private final Propagator propagator;
    
        public TracingInterceptorHC4(Tracer tracer, Propagator propagator) {
            this.tracer = tracer;
            this.propagator = propagator;
        }
    
        @Override
        public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
            log.debug("Before adding tracing headers: {}", Arrays.toString(request.getAllHeaders()));
            if (tracer.currentSpan() != null) {
                propagator.inject(tracer.currentSpan().context(), request, HttpMessage::addHeader);
            } else {
                log.debug("Current span is absent. Hence skipping tracing headers injection");
            }
            log.debug("After adding tracing headers: {}", Arrays.toString(request.getAllHeaders()));
        }
    }
    

    Registering the interceptor while creating client:

    return HttpClients.custom()
            .addInterceptorLast( new TracingInterceptorHC4(tracer, propagator))
            .build();
    

    tracer and propagator are taken from Spring container by autowiring.


    Previous answer:

    I was able to instrument http client by building it the following way:

    final MicrometerHttpRequestExecutor httpRequestExecutor = MicrometerHttpRequestExecutor
            .builder(new SimpleMeterRegistry())
            .uriMapper(x -> x.getRequestLine().getUri())
            .observationRegistry(observationRegistry)
            .build();
    
    return HttpClients.custom()
            .setRequestExecutor(httpRequestExecutor)
            .build();
    

    ObservationRegistry is autowired.

    There is support for both apache HC4 and HC5. Import accordingly.

    I also had missed adding the below properties.

    management.tracing.propagation.consume=B3_MULTI
    management.tracing.propagation.produce=B3_MULTI