How do I make spring boot RestTemplate client metrics not create a new tag for query parameters

Viewed 1928

I've got a spring boot application that is defining a RestTemplate bean as follows:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder.build();
}

Also, pulling in spring-boot-starter-actuator and io.micrometer:micrometer-registry-prometheus.

When I use the injected RestTemplate as follows:

@Autowired
private RestTemplate restTemplate;

private String uriTemplate = "http://my.domain.com/bookstore-api/books";

public List<Book> getBooksByAuthor(String author) {
    // create URI for "http://my.domain.com/bookstore-api/books?author={authorId}"
    UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder
            .fromUriString(uriTemplate)
            .queryParam("author", author);

    // make the GET 
    ResponseEntity<Book[]> responseEntity = restTemplate.getForEntity(uriComponentsBuilder.toUriString(),Book[].class);

    // rest ommitted for brevity
}

When getBooksByAuthor("Tolkien") is called, we can then hit /metrics/prometheus and see the following:

http_client_requests_seconds_count{clientName="my.domain.com",method="GET",status="200",uri="/bookstore-api/books?author=Tolkien",} 2.0
http_client_requests_seconds_sum{clientName="my.domain.com",method="GET",status="200",uri="/bookstore-api/books?author=Tolkien",} 0.253227898

This would be fine, except that there are lots of authors out there, and eventually I will get the "too many tags" exception.

I would prefer to have the following (similar to how path variables get templated):

http_client_requests_seconds_count{clientName="my.domain.com",method="GET",status="200",uri="/bookstore-api/books?author={author}",} 2.0
http_client_requests_seconds_sum{clientName="my.domain.com",method="GET",status="200",uri="/bookstore-api/books?author={author}",} 0.253227898

Is this possible to achieve by modifying the way I use UriComponentsBuilder? The closest thing I've found is to define my own RestTemplateExchangeTagsProvider, and override the default to do some crazy regex replacement.

2 Answers

Just fixed same issue in SpringBoot 2.4.5 using:

responseEntity = restTemplate.exchange(
                config.getDataUrl(),
                HttpMethod.GET,
                httpEntity,
                new ParameterizedTypeReference<String>() {},
                rowId);

where getDataUrl resolves to:

https://data-service-dev.apps.cloud.net/api/hbase/getData?rowId={rowId}

metrics:

http_client_requests_seconds_count{clientName="data-service-dev.apps.cloud.net",method="GET",outcome="SUCCESS",status="200",uri="/api/hbase/getData?rowId={rowId}",} 1.0
...

I have had a same problem. Maybe this information will help you.

In my case restTemplate.setUriTemplateHandler(handler) had overwritten annonymous MetricsClientHttpRequestInterceptor#createUriTemplateHandler. And the original url templates had not been stored into memory for prometheus.

DefaultUriBuilderFactory builderFactory = new DefaultUriBuilderFactory();
builderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);

customizer.customize(restTemplate);
restTemplate.setUriTemplateHandler(handler);

So, I changed order of the commands:

restTemplate.setUriTemplateHandler(handler);
customizer.customize(restTemplate);

Please check that there are no setting commands for restTemplate after MetricsClientHttpRequestInterceptor.customize(restTemplate).

Related