Allow custom observation convention for RestClient
This commit allows to use a custom `ObservationConvention` in the `DefaultRestClient`, and to set it through the `RestClient.Builder`. Closes gh-31325
This commit is contained in:
		
							parent
							
								
									c356ce2637
								
							
						
					
					
						commit
						9cab6c90a9
					
				| 
						 | 
				
			
			@ -107,6 +107,9 @@ final class DefaultRestClient implements RestClient {
 | 
			
		|||
 | 
			
		||||
	private final ObservationRegistry observationRegistry;
 | 
			
		||||
 | 
			
		||||
	@Nullable
 | 
			
		||||
	private final ClientRequestObservationConvention observationConvention;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	DefaultRestClient(ClientHttpRequestFactory clientRequestFactory,
 | 
			
		||||
			@Nullable List<ClientHttpRequestInterceptor> interceptors,
 | 
			
		||||
| 
						 | 
				
			
			@ -116,6 +119,7 @@ final class DefaultRestClient implements RestClient {
 | 
			
		|||
			@Nullable List<StatusHandler> statusHandlers,
 | 
			
		||||
			List<HttpMessageConverter<?>> messageConverters,
 | 
			
		||||
			ObservationRegistry observationRegistry,
 | 
			
		||||
			@Nullable ClientRequestObservationConvention observationConvention,
 | 
			
		||||
			DefaultRestClientBuilder builder) {
 | 
			
		||||
 | 
			
		||||
		this.clientRequestFactory = clientRequestFactory;
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +130,7 @@ final class DefaultRestClient implements RestClient {
 | 
			
		|||
		this.defaultStatusHandlers = (statusHandlers != null ? new ArrayList<>(statusHandlers) : new ArrayList<>());
 | 
			
		||||
		this.messageConverters = messageConverters;
 | 
			
		||||
		this.observationRegistry = observationRegistry;
 | 
			
		||||
		this.observationConvention = observationConvention;
 | 
			
		||||
		this.builder = builder;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -393,7 +398,7 @@ final class DefaultRestClient implements RestClient {
 | 
			
		|||
				clientRequest.getHeaders().addAll(headers);
 | 
			
		||||
				ClientRequestObservationContext observationContext = new ClientRequestObservationContext(clientRequest);
 | 
			
		||||
				observationContext.setUriTemplate((String) this.attributes.get(URI_TEMPLATE_ATTRIBUTE));
 | 
			
		||||
				observation = ClientHttpObservationDocumentation.HTTP_CLIENT_EXCHANGES.observation(null,
 | 
			
		||||
				observation = ClientHttpObservationDocumentation.HTTP_CLIENT_EXCHANGES.observation(observationConvention,
 | 
			
		||||
						DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, observationRegistry).start();
 | 
			
		||||
				if (this.body != null) {
 | 
			
		||||
					this.body.writeTo(clientRequest);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
 | 
			
		|||
import org.springframework.http.client.JdkClientHttpRequestFactory;
 | 
			
		||||
import org.springframework.http.client.JettyClientHttpRequestFactory;
 | 
			
		||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
 | 
			
		||||
import org.springframework.http.client.observation.ClientRequestObservationConvention;
 | 
			
		||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.HttpMessageConverter;
 | 
			
		||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +133,9 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
 | 
			
		|||
 | 
			
		||||
	private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
 | 
			
		||||
 | 
			
		||||
	@Nullable
 | 
			
		||||
	private ClientRequestObservationConvention observationConvention;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	public DefaultRestClientBuilder() {
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -161,6 +165,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
 | 
			
		|||
		this.interceptors = (other.interceptors != null) ? new ArrayList<>(other.interceptors) : null;
 | 
			
		||||
		this.initializers = (other.initializers != null) ? new ArrayList<>(other.initializers) : null;
 | 
			
		||||
		this.observationRegistry = other.observationRegistry;
 | 
			
		||||
		this.observationConvention = other.observationConvention;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public DefaultRestClientBuilder(RestTemplate restTemplate) {
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +187,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
 | 
			
		|||
			this.initializers = new ArrayList<>(restTemplate.getClientHttpRequestInitializers());
 | 
			
		||||
		}
 | 
			
		||||
		this.observationRegistry = restTemplate.getObservationRegistry();
 | 
			
		||||
		this.observationConvention = restTemplate.getObservationConvention();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -307,6 +313,12 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
 | 
			
		|||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public RestClient.Builder observationConvention(ClientRequestObservationConvention observationConvention) {
 | 
			
		||||
		this.observationConvention = observationConvention;
 | 
			
		||||
		return this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public RestClient.Builder apply(Consumer<RestClient.Builder> builderConsumer) {
 | 
			
		||||
		builderConsumer.accept(this);
 | 
			
		||||
| 
						 | 
				
			
			@ -362,6 +374,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
 | 
			
		|||
				this.statusHandlers,
 | 
			
		||||
				messageConverters,
 | 
			
		||||
				this.observationRegistry,
 | 
			
		||||
				this.observationConvention,
 | 
			
		||||
				new DefaultRestClientBuilder(this)
 | 
			
		||||
				);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ import org.springframework.http.client.ClientHttpRequestFactory;
 | 
			
		|||
import org.springframework.http.client.ClientHttpRequestInitializer;
 | 
			
		||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
 | 
			
		||||
import org.springframework.http.client.ClientHttpResponse;
 | 
			
		||||
import org.springframework.http.client.observation.ClientRequestObservationConvention;
 | 
			
		||||
import org.springframework.http.converter.HttpMessageConverter;
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.web.util.DefaultUriBuilderFactory;
 | 
			
		||||
| 
						 | 
				
			
			@ -374,6 +375,16 @@ public interface RestClient {
 | 
			
		|||
		 */
 | 
			
		||||
		Builder observationRegistry(ObservationRegistry observationRegistry);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Configure the {@link io.micrometer.observation.ObservationConvention} to use
 | 
			
		||||
		 * for collecting metadata for the request observation. Will use
 | 
			
		||||
		 * {@link org.springframework.http.client.observation.DefaultClientRequestObservationConvention}
 | 
			
		||||
		 * if none provided.
 | 
			
		||||
		 * @param observationConvention the observation convention to use
 | 
			
		||||
		 * @return this builder
 | 
			
		||||
		 */
 | 
			
		||||
		Builder observationConvention(ClientRequestObservationConvention observationConvention);
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Apply the given {@code Consumer} to this builder instance.
 | 
			
		||||
		 * <p>This can be useful for applying pre-packaged customizations.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -375,6 +375,15 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
 | 
			
		|||
		this.observationConvention = observationConvention;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return the configured {@link ClientRequestObservationConvention}, or {@code null} if not set.
 | 
			
		||||
	 * @since 6.1
 | 
			
		||||
	 */
 | 
			
		||||
	@Nullable
 | 
			
		||||
	public ClientRequestObservationConvention getObservationConvention() {
 | 
			
		||||
		return this.observationConvention;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// GET
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,8 @@ import org.springframework.http.client.ClientHttpRequest;
 | 
			
		|||
import org.springframework.http.client.ClientHttpRequestFactory;
 | 
			
		||||
import org.springframework.http.client.ClientHttpResponse;
 | 
			
		||||
import org.springframework.http.client.observation.ClientRequestObservationContext;
 | 
			
		||||
import org.springframework.http.client.observation.ClientRequestObservationConvention;
 | 
			
		||||
import org.springframework.http.client.observation.DefaultClientRequestObservationConvention;
 | 
			
		||||
import org.springframework.http.converter.HttpMessageConverter;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
| 
						 | 
				
			
			@ -159,6 +161,20 @@ class RestClientObservationTests {
 | 
			
		|||
		assertThatHttpObservation().hasLowCardinalityKeyValue("outcome", "UNKNOWN");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void executeWithCustomConventionUsesCustomObservationName() throws Exception {
 | 
			
		||||
		ClientRequestObservationConvention observationConvention =
 | 
			
		||||
				new DefaultClientRequestObservationConvention("custom.requests");
 | 
			
		||||
		RestClient restClient = this.client.mutate().observationConvention(observationConvention).build();
 | 
			
		||||
		mockSentRequest(GET, "https://example.org");
 | 
			
		||||
		mockResponseStatus(HttpStatus.OK);
 | 
			
		||||
 | 
			
		||||
		restClient.get().uri("https://example.org").retrieve().toBodilessEntity();
 | 
			
		||||
 | 
			
		||||
		TestObservationRegistryAssert.assertThat(this.observationRegistry)
 | 
			
		||||
				.hasObservationWithNameEqualTo("custom.requests");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	private void mockSentRequest(HttpMethod method, String uri) throws Exception {
 | 
			
		||||
		mockSentRequest(method, uri, new HttpHeaders());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue