WebClient metrics with uri template when using UriBuilder
See original GitHub issueHi,
Here are the versions this discussion is related to :
- Spring 5.1.4.RELEASE
- SpringBoot 2.1.2.RELEASE
- Micrometer 1.1.2
Using reactive WebClient with micrometer give the ability to expose a lot of interesting metrics (such as number of requests, duration, …). As describe in the official documentation, metrics generated by an instrumented client are tagged with the request’s URI template prior to variable substitution, if possible (for example, /api/person/{id}).
Indeed, it appears that the URI template cannot be used when specifying uri using UriBuilder
. So doing this :
webclient.get().uri("/api/person/{id}", "1234")...
will generate a metric with the expected uri template (eg : /api/person/{id}), but using uri builder like this :
webclient.get().uri(uriBuilder -> uriBuilder.path("/api/person/{id}").build("1234"))
will generate metrics using the substituted uri (eg : /api/person/1234).
This is annoying cause we can have lot of possible uris and we loose precious information (and I think the uri builder is the smartest way to forge uris).
Looking closer, it appears that there is no URI_TEMPLATE_ATTRIBUTE
attribute set when using UriBuilder
with WebClient
.
Digging deeper, I discovered that the current implementation does not take care about baseUri
set on the WebClient
. So doing this :
webClientBuilder().baseUri("https://foo.com/api").build().get().uri("/person/{id}", "1234")...
Will generate a metric using /person/{id}
instead of /api/person/{id}
.
I suggest to make few changes on UriBuilder
to be able to get access to the original uri template (add a getUriTemplate
method). This way we could adapt the DefaultWebClient
like this :
@Override
public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
UriBuilder uriBuilder = uriBuilderFactory.uriString(uriTemplate);
attribute(URI_TEMPLATE_ATTRIBUTE, uriBuilder.getUriTemplate());
return uri(uriBuilder.build(uriVariables));
}
@Override
public RequestBodySpec uri(String uriTemplate, Map<String, ?> uriVariables) {
UriBuilder uriBuilder = uriBuilderFactory.uriString(uriTemplate);
attribute(URI_TEMPLATE_ATTRIBUTE, uriBuilder.getUriTemplate());
return uri(uriBuilder.build(uriVariables));
}
@Override
public RequestBodySpec uri(Function<UriBuilder, URI> uriFunction) {
UriBuilder uriBuilder = uriBuilderFactory.builder();
RequestBodySpec uri = uri(uriFunction.apply(uriBuilder));
attribute(URI_TEMPLATE_ATTRIBUTE, uriBuilder.getUriTemplate());
return uri;
}
This solution seems very simple but I would like to know if you think this is a good approach and if it make sense for you ?
If you want I can make a pull request (around 10 lines of code).
Keep me in touch.
Issue Analytics
- State:
- Created 5 years ago
- Comments:10 (5 by maintainers)
Thanks @bclozel. I opened https://github.com/spring-projects/spring-boot/issues/22832 and referenced this one.
Hi,
I don’t know what I can provide to demonstrate that the current implementation does not take care about
baseUri
. If you take a look at the current implementation of theuri(String uriTemplate, Object... uriVariables)
method onDefaultWebClient
You can see, the that
attribute(URI_TEMPLATE_ATTRIBUTE, uriTemplate);
only take theuriTemplate
value. This basically demonstrate that if you do thiswebClientBuilder().baseUri("https://foo.com/api").build().get().uri("/person/{id}", "1234")
you will haveURI_TEMPLATE_ATTRIBUTE
set to/person/{id}
instead of/api/person/{id}
right ?About the suggested implementation you noticed :
It is normal this code does not compile cause it was just an example of what the code could be if the
UriBuilder
interface were modified with a newgetUriTemplate
method to give access to the not expanded uri.May be a complete pull request could be more clear ?