Skip to content

WebClient metrics with uri template when using UriBuilder #22371

@jhaeyaert

Description

@jhaeyaert

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: webIssues in web modules (web, webmvc, webflux, websocket)status: supersededAn issue that has been superseded by anothertype: enhancementA general enhancement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions