Extend observations to RestClient ResponseSpec
Prior to this commit, the `RestClient` observations would be stopped as soon as the exchange function was called. This means that all errors related to response decoding or mapping would not be recorded by the obsevations. This commit extends the observation recording to the `ResponseSpec` DSL calls as well as custom exchange functions. Fixes gh-32575
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.web.client;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -40,6 +41,7 @@ import org.springframework.http.client.observation.ClientRequestObservationConte
|
||||
import org.springframework.http.client.observation.ClientRequestObservationConvention;
|
||||
import org.springframework.http.client.observation.DefaultClientRequestObservationConvention;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -84,7 +86,7 @@ class RestClientObservationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeVarArgsAddsUriTemplateAsKeyValue() throws Exception {
|
||||
void shouldContributeTemplateWhenUriVariables() throws Exception {
|
||||
mockSentRequest(GET, "https://example.com/hotels/42/bookings/21");
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
|
||||
@@ -95,7 +97,7 @@ class RestClientObservationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeArgsMapAddsUriTemplateAsKeyValue() throws Exception {
|
||||
void shouldContributeTemplateWhenMap() throws Exception {
|
||||
mockSentRequest(GET, "https://example.com/hotels/42/bookings/21");
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
|
||||
@@ -107,9 +109,8 @@ class RestClientObservationTests {
|
||||
assertThatHttpObservation().hasLowCardinalityKeyValue("uri", "/hotels/{hotel}/bookings/{booking}");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void executeAddsSuccessAsOutcome() throws Exception {
|
||||
void shouldContributeSuccessOutcome() throws Exception {
|
||||
mockSentRequest(GET, "https://example.org");
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
mockResponseBody("Hello World", MediaType.TEXT_PLAIN);
|
||||
@@ -120,7 +121,7 @@ class RestClientObservationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeAddsServerErrorAsOutcome() throws Exception {
|
||||
void shouldContributeServerErrorOutcome() throws Exception {
|
||||
String url = "https://example.org";
|
||||
mockSentRequest(GET, url);
|
||||
mockResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
@@ -134,7 +135,7 @@ class RestClientObservationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeAddsExceptionAsKeyValue() throws Exception {
|
||||
void shouldContributeDecodingError() throws Exception {
|
||||
mockSentRequest(POST, "https://example.org/resource");
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
|
||||
@@ -150,7 +151,7 @@ class RestClientObservationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeWithIoExceptionAddsUnknownOutcome() throws Exception {
|
||||
void shouldContributeIOError() throws Exception {
|
||||
String url = "https://example.org/resource";
|
||||
mockSentRequest(GET, url);
|
||||
given(request.execute()).willThrow(new IOException("Socket failure"));
|
||||
@@ -161,7 +162,7 @@ class RestClientObservationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeWithCustomConventionUsesCustomObservationName() throws Exception {
|
||||
void shouldUseCustomConvention() throws Exception {
|
||||
ClientRequestObservationConvention observationConvention =
|
||||
new DefaultClientRequestObservationConvention("custom.requests");
|
||||
RestClient restClient = this.client.mutate().observationConvention(observationConvention).build();
|
||||
@@ -174,6 +175,56 @@ class RestClientObservationTests {
|
||||
.hasObservationWithNameEqualTo("custom.requests");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAddClientDecodingErrorAsException() throws Exception {
|
||||
String url = "https://example.org";
|
||||
mockSentRequest(GET, url);
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
mockResponseBody("INVALID", MediaType.APPLICATION_JSON);
|
||||
|
||||
assertThatExceptionOfType(RestClientException.class).isThrownBy(() ->
|
||||
client.get().uri(url).retrieve().body(User.class));
|
||||
|
||||
assertThatHttpObservation().hasLowCardinalityKeyValue("exception", "RestClientException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAddUnknownContentTypeErrorAsException() throws Exception {
|
||||
String url = "https://example.org";
|
||||
mockSentRequest(GET, url);
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
mockResponseBody("Not Found", MediaType.TEXT_HTML);
|
||||
|
||||
assertThatExceptionOfType(RestClientException.class).isThrownBy(() ->
|
||||
client.get().uri(url).retrieve().body(User.class));
|
||||
|
||||
assertThatHttpObservation().hasLowCardinalityKeyValue("exception", "UnknownContentTypeException");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerObservationWhenReadingBody() throws Exception {
|
||||
mockSentRequest(GET, "https://example.org");
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
mockResponseBody("Hello World", MediaType.TEXT_PLAIN);
|
||||
|
||||
client.get().uri("https://example.org").exchange((request, response) -> response.bodyTo(String.class));
|
||||
assertThatHttpObservation().hasLowCardinalityKeyValue("outcome", "SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerObservationWhenReadingStream() throws Exception {
|
||||
mockSentRequest(GET, "https://example.org");
|
||||
mockResponseStatus(HttpStatus.OK);
|
||||
mockResponseBody("Hello World", MediaType.TEXT_PLAIN);
|
||||
|
||||
client.get().uri("https://example.org").exchange((request, response) -> {
|
||||
String result = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
|
||||
response.close();
|
||||
return result;
|
||||
}, false);
|
||||
assertThatHttpObservation().hasLowCardinalityKeyValue("outcome", "SUCCESS");
|
||||
}
|
||||
|
||||
|
||||
private void mockSentRequest(HttpMethod method, String uri) throws Exception {
|
||||
mockSentRequest(method, uri, new HttpHeaders());
|
||||
@@ -220,4 +271,8 @@ class RestClientObservationTests {
|
||||
}
|
||||
}
|
||||
|
||||
record User(String name) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user