URI tag of http.client.requests metric as NONE in Spring Boot 3.0.X
Asked Answered
A

2

6

After migrate from spring boot 2.7.5 to spring boot 3.0.1.

The tag URI on the metric http.client.requests have the value none in all cases.

We build the URI of the restTemplate like

URI uri = UriComponentsBuilder.fromUri(restTemplate.getUriTemplateHandler().expand("/hello"))
   .build()
   .toUri();

return restTemplate.getForObject(uri, String.class);

I'm used to do it this way since to add query params, I think it's more easy to read and to manage.

To reproduce it I created two small application in this repository one on spring boot 2.7.7 and an other one on 3.0.1

I figure out it's linked to the new Observability on spring 6 and linked to this issue on spring boot.

And Spring boot 2 customise the UriTemplateHandler and store the urlTemplate in ThreadLocal<Deque>. And use this deque for the tag uri.

From the documentation I know I could enhance the tag by providing a new @Bean ClientRequestObservationConvention.

I could also use org.springframework.web.client.RestTemplate#getForObject(java.lang.String, java.lang.Class, java.util.Map<java.lang.String,?>). like

return restTemplate.getForObject("/hello", String.class);

But I wonder if there is a way to get the previous behaviour and still used org.springframework.web.client.RestTemplate#getForEntity(java.net.URI, java.lang.Class).

Airdrome answered 30/12, 2022 at 13:59 Comment(0)
G
2

Prior to Spring Boot 3 and the Observability improvements, RestTemplate would indeed use the full request URI for the "uri" tag. This is wrong, as URI values are not bounded in an application and can cause cardinality explosion in the metrics system.

For example, a client could report uri tags with "/user/12", "/user/23" etc. The correct tag for this should be low cardinality and use a request pattern like "/user/{userId}".

In Spring Boot 2.x, for compatibility reasons, we did not alter this behavior but added instead a MeterFilter that stops recording metrics once a threshold is reached. As of Spring Framework 6, the instrumentation is built in RestTemplate directly. This tag is only recorded if a RestTemplate method accepting a String template, like restTemplate.getForObject("/hello", String.class) as you've noticed.

You can revert to the previous behavior by providing your own ClientRequestObservationConvention (see the migration wiki for more on that) but note that you'll also need to ensure that cardinality explosion doesn't become an issue in your application.

See this Spring Boot issue that looks quite similar.

Gallnut answered 4/1, 2023 at 19:24 Comment(1)
Thanks for the answer. I tried to enhance my question to show what behaviour changed. Mostly I want to show if you build the uri based on "restTemplate.getUriTemplateHandler". The tag URI was working properly thanks to the MetricsRestTemplateCustomizer. But I suppose using a ThreadLocal variable is not something that spring-boot wanted to keep for this usage. Or it might be subject for spring framework to enhance the RestTemplate.Airdrome
A
1

He is some example code to get URI metric tag working again in Spring Boot 3. Define the ExtendedServerRequestObservationConvention class.

import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;

public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention {

    @Override
    public KeyValues getLowCardinalityKeyValues(final ServerRequestObservationContext context) {
        return super.getLowCardinalityKeyValues(context).and(custom(context));
    }

    protected KeyValue custom(final ServerRequestObservationContext context) {
        return KeyValue.of("uri", context.getCarrier().getRequestURI());
}

}

Then add a bean so that Spring creates an instance of this class

@Bean
    public ExtendedServerRequestObservationConvention extendedServerRequestObservationConvention() {
        return new ExtendedServerRequestObservationConvention();
}
Anselmo answered 18/4 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.