Start RestTemplate observation wtih available request

Prior to this commit, the `RestTemplate` `ClientHttpObservation` would
be started before the request object is available. While this would also
measure the connection estalishment for some HTTP client libraries, this
arrangement is incompatible with a tracing approach where the request
must be available to propagate information through the request headers.

This commit ensures that the observation only starts when the request is
available.

Fixes gh-29234
This commit is contained in:
Brian Clozel
2022-09-30 23:12:53 +02:00
parent 380795e5b8
commit a18c692b53
4 changed files with 51 additions and 37 deletions

View File

@@ -36,8 +36,13 @@ public class ClientHttpObservationContext extends RequestReplySenderContext<Clie
@Nullable
private String uriTemplate;
public ClientHttpObservationContext() {
/**
* Create an observation context for {@link ClientHttpRequest} observations.
* @param request the HTTP client request
*/
public ClientHttpObservationContext(ClientHttpRequest request) {
super(ClientHttpObservationContext::setRequestHeader);
this.setCarrier(request);
}
private static void setRequestHeader(@Nullable ClientHttpRequest request, String name, String value) {

View File

@@ -839,14 +839,20 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
Assert.notNull(url, "url is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpObservationContext observationContext = new ClientHttpObservationContext();
ClientHttpRequest request;
try {
request = createRequest(url, method);
}
catch (IOException ex) {
ResourceAccessException exception = createResourceAccessException(url, method, ex);
throw exception;
}
ClientHttpObservationContext observationContext = new ClientHttpObservationContext(request);
observationContext.setUriTemplate(uriTemplate);
Observation observation = ClientHttpObservation.HTTP_REQUEST.observation(this.observationConvention,
DEFAULT_OBSERVATION_CONVENTION, observationContext, this.observationRegistry).start();
observationContext.setUriTemplate(uriTemplate);
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
observationContext.setCarrier(request);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
@@ -856,11 +862,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
ResourceAccessException exception = new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
ResourceAccessException exception = createResourceAccessException(url, method, ex);
observation.error(exception);
throw exception;
}
@@ -876,6 +878,15 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
}
}
protected static ResourceAccessException createResourceAccessException(URI url, HttpMethod method, IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
ResourceAccessException exception = new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
return exception;
}
/**
* Handle the given response, performing appropriate logging and
* invoking the {@link ResponseErrorHandler} if necessary.