question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

WebClient metrics with uri template when using UriBuilder

See original GitHub issue

Hi,

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:closed
  • Created 5 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
nhmarujocommented, Aug 7, 2020
1reaction
jhaeyaertcommented, Feb 8, 2019

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 the uri(String uriTemplate, Object... uriVariables) method on DefaultWebClient

	@Override
	public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
		attribute(URI_TEMPLATE_ATTRIBUTE, uriTemplate);
		return uri(uriBuilderFactory.expand(uriTemplate, uriVariables));
	}

You can see, the that attribute(URI_TEMPLATE_ATTRIBUTE, uriTemplate); only take the uriTemplate value. This basically demonstrate that if you do this webClientBuilder().baseUri("https://foo.com/api").build().get().uri("/person/{id}", "1234") you will have URI_TEMPLATE_ATTRIBUTE set to /person/{id} instead of /api/person/{id} right ?

About the suggested implementation you noticed :

	@Override
	public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
		UriBuilder uriBuilder = uriBuilderFactory.uriString(uriTemplate);
		attribute(URI_TEMPLATE_ATTRIBUTE, uriBuilder.getUriTemplate());
		return uri(uriBuilder.build(uriVariables));
	}

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 new getUriTemplate method to give access to the not expanded uri.

May be a complete pull request could be more clear ?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Controlling Spring WebClient metrics cardinality - Medium
This key's value (the URI template) is used by WebClientExchangeTags.uri(ClientRequest request) when creating the tag for the metric (in the ...
Read more >
Add URI Parameters to Spring WebClient Requests - amitph
A guide to add Request URI parameters - path parameters & query parameters to the requests made by Spring WebFlux WebClient.
Read more >
How to aggregate metrics across URIs in webclient?
I want to collect metrics across endpoints. Currently, every uri is collected separately. Is there is a way to configure ...
Read more >
Avoiding memory leaks with Spring Boot WebClient | bol.com
In this blog post we'll explain how to avoid memory leaks with Spring Boot WebClient and why it is better to avoid the...
Read more >
Expose Metrics of Spring WebClient using Spring Boot Actuator
Learn how to expose metrics of your HTTP client using Spring WebClient: e.g. request URI, response codes, duration of request...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found