Browse Source

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
pull/31351/head
Henning Poettker 1 year ago committed by Brian Clozel
parent
commit
9cab6c90a9
  1. 7
      spring-web/src/main/java/org/springframework/web/client/DefaultRestClient.java
  2. 13
      spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java
  3. 11
      spring-web/src/main/java/org/springframework/web/client/RestClient.java
  4. 9
      spring-web/src/main/java/org/springframework/web/client/RestTemplate.java
  5. 16
      spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java

7
spring-web/src/main/java/org/springframework/web/client/DefaultRestClient.java

@ -107,6 +107,9 @@ final class DefaultRestClient implements RestClient {
private final ObservationRegistry observationRegistry; private final ObservationRegistry observationRegistry;
@Nullable
private final ClientRequestObservationConvention observationConvention;
DefaultRestClient(ClientHttpRequestFactory clientRequestFactory, DefaultRestClient(ClientHttpRequestFactory clientRequestFactory,
@Nullable List<ClientHttpRequestInterceptor> interceptors, @Nullable List<ClientHttpRequestInterceptor> interceptors,
@ -116,6 +119,7 @@ final class DefaultRestClient implements RestClient {
@Nullable List<StatusHandler> statusHandlers, @Nullable List<StatusHandler> statusHandlers,
List<HttpMessageConverter<?>> messageConverters, List<HttpMessageConverter<?>> messageConverters,
ObservationRegistry observationRegistry, ObservationRegistry observationRegistry,
@Nullable ClientRequestObservationConvention observationConvention,
DefaultRestClientBuilder builder) { DefaultRestClientBuilder builder) {
this.clientRequestFactory = clientRequestFactory; this.clientRequestFactory = clientRequestFactory;
@ -126,6 +130,7 @@ final class DefaultRestClient implements RestClient {
this.defaultStatusHandlers = (statusHandlers != null ? new ArrayList<>(statusHandlers) : new ArrayList<>()); this.defaultStatusHandlers = (statusHandlers != null ? new ArrayList<>(statusHandlers) : new ArrayList<>());
this.messageConverters = messageConverters; this.messageConverters = messageConverters;
this.observationRegistry = observationRegistry; this.observationRegistry = observationRegistry;
this.observationConvention = observationConvention;
this.builder = builder; this.builder = builder;
} }
@ -393,7 +398,7 @@ final class DefaultRestClient implements RestClient {
clientRequest.getHeaders().addAll(headers); clientRequest.getHeaders().addAll(headers);
ClientRequestObservationContext observationContext = new ClientRequestObservationContext(clientRequest); ClientRequestObservationContext observationContext = new ClientRequestObservationContext(clientRequest);
observationContext.setUriTemplate((String) this.attributes.get(URI_TEMPLATE_ATTRIBUTE)); 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(); DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, observationRegistry).start();
if (this.body != null) { if (this.body != null) {
this.body.writeTo(clientRequest); this.body.writeTo(clientRequest);

13
spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java

@ -35,6 +35,7 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.JdkClientHttpRequestFactory; import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.http.client.JettyClientHttpRequestFactory; import org.springframework.http.client.JettyClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.client.observation.ClientRequestObservationConvention;
import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter; import org.springframework.http.converter.ResourceHttpMessageConverter;
@ -132,6 +133,9 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP; private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
@Nullable
private ClientRequestObservationConvention observationConvention;
public DefaultRestClientBuilder() { public DefaultRestClientBuilder() {
} }
@ -161,6 +165,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
this.interceptors = (other.interceptors != null) ? new ArrayList<>(other.interceptors) : null; this.interceptors = (other.interceptors != null) ? new ArrayList<>(other.interceptors) : null;
this.initializers = (other.initializers != null) ? new ArrayList<>(other.initializers) : null; this.initializers = (other.initializers != null) ? new ArrayList<>(other.initializers) : null;
this.observationRegistry = other.observationRegistry; this.observationRegistry = other.observationRegistry;
this.observationConvention = other.observationConvention;
} }
public DefaultRestClientBuilder(RestTemplate restTemplate) { public DefaultRestClientBuilder(RestTemplate restTemplate) {
@ -182,6 +187,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
this.initializers = new ArrayList<>(restTemplate.getClientHttpRequestInitializers()); this.initializers = new ArrayList<>(restTemplate.getClientHttpRequestInitializers());
} }
this.observationRegistry = restTemplate.getObservationRegistry(); this.observationRegistry = restTemplate.getObservationRegistry();
this.observationConvention = restTemplate.getObservationConvention();
} }
@ -307,6 +313,12 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
return this; return this;
} }
@Override
public RestClient.Builder observationConvention(ClientRequestObservationConvention observationConvention) {
this.observationConvention = observationConvention;
return this;
}
@Override @Override
public RestClient.Builder apply(Consumer<RestClient.Builder> builderConsumer) { public RestClient.Builder apply(Consumer<RestClient.Builder> builderConsumer) {
builderConsumer.accept(this); builderConsumer.accept(this);
@ -362,6 +374,7 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
this.statusHandlers, this.statusHandlers,
messageConverters, messageConverters,
this.observationRegistry, this.observationRegistry,
this.observationConvention,
new DefaultRestClientBuilder(this) new DefaultRestClientBuilder(this)
); );
} }

11
spring-web/src/main/java/org/springframework/web/client/RestClient.java

@ -42,6 +42,7 @@ import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInitializer; import org.springframework.http.client.ClientHttpRequestInitializer;
import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.observation.ClientRequestObservationConvention;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.DefaultUriBuilderFactory;
@ -374,6 +375,16 @@ public interface RestClient {
*/ */
Builder observationRegistry(ObservationRegistry observationRegistry); 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. * Apply the given {@code Consumer} to this builder instance.
* <p>This can be useful for applying pre-packaged customizations. * <p>This can be useful for applying pre-packaged customizations.

9
spring-web/src/main/java/org/springframework/web/client/RestTemplate.java

@ -375,6 +375,15 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
this.observationConvention = observationConvention; 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 // GET
@Override @Override

16
spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java

@ -37,6 +37,8 @@ import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.observation.ClientRequestObservationContext; 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 org.springframework.http.converter.HttpMessageConverter;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -159,6 +161,20 @@ class RestClientObservationTests {
assertThatHttpObservation().hasLowCardinalityKeyValue("outcome", "UNKNOWN"); 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 { private void mockSentRequest(HttpMethod method, String uri) throws Exception {
mockSentRequest(method, uri, new HttpHeaders()); mockSentRequest(method, uri, new HttpHeaders());

Loading…
Cancel
Save