spring-bootspring-webclient

(URI tag not populated) Metrics on Spring Boot 3.1.5 and WebClient


I just made a migration of a project running on Spring Boot 2.7 to Spring Boot 3.1.

I don't have an issue with my metrics when using RestTemplate but with WebClient. I don't have my URI tag populated.

  @Bean
  public MyClient myClient(
      ClientProperties clientProperties,
      WebClient.Builder webClientBuilder) {
    return new MyClient(webClientBuilder, clientProperties);
  }

I'm defining a client and I'm putting a WebClient.Builder in the method arguments so that Spring uses the one it has auto configured (Spring Documentation).

Spring Boot Actuator manages the instrumentation of both RestTemplate and WebClient. For that, you have to inject the auto-configured builder and use it to create instances:

  • RestTemplateBuilder for RestTemplate
  • WebClient.Builder for WebClient

When I'm calling an API using WebClient. It does something as it saves a metric in my actuator endpoint but I don't have my uri tag populated anymore, I have a none value :

Actuator http.client.requests metrics endpoint

Any clue ? I tried defining my own ObservationWebClientCustomizer but with no success.


Solution

  • I have a workaround for my issue, I'm adding a new ClientRequestObservationConvention where I can update the URI tag value.

      @Bean
      public MyClient myClient(
          ClientProperties clientProperties, WebClient.Builder webClientBuilder) {
        webClientBuilder.observationConvention(new CustomizedClientRequestObservationConvention());
        return new Client(webClientBuilder, clientProperties);
      }
    

    My custom ClientRequestObservation :

    public class CustomizedClientRequestObservationConvention
        extends DefaultClientRequestObservationConvention {
    
    
      public CustomizedClientRequestObservationConvention() {
        super();
      }
    
      @Override
      protected KeyValue uri(ClientRequestObservationContext context) {
        if (context.getCarrier() != null) {
          ClientRequest request = context.getCarrier().build();
          return KeyValue.of(LowCardinalityKeyNames.URI, request.url().getPath());
        }
        return KeyValue.of(LowCardinalityKeyNames.URI, KeyValue.NONE_VALUE);
      }
    }
    

    Strangely, in my case context.getRequest() was null. I'm using context.getCarrier() to have access to the request attributes.

    I'm posting this in case someone has the same problem as me. And if someone have a better solution, that'll be great.