RestTemplate set error to observation only for IOException and RestClientException and there is no way to customize that.
I tried error handler like this
@RequiredArgsConstructor
public class DefaultResponseRestTemplateErrorHandler extends DefaultResponseErrorHandler {
private final ObservationRegistry observationRegistry;
@Override
public void handleError(@NonNull ClientHttpResponse clientHttpResponse) throws IOException {
final var status = (HttpStatus) clientHttpResponse.getStatusCode();
final var exception = switch (status) {
case BAD_REQUEST -> new RuntimeException("bad request");
case UNAUTHORIZED -> new RuntimeException("unauthorized");
case FORBIDDEN -> new RuntimeException("forbidden");
case GONE -> new RuntimeException("gone");
default -> new RuntimeException("other");
};
Optional.ofNullable(observationRegistry.getCurrentObservation())
.map(Observation::getContext)
.filter(ClientRequestObservationContext.class::isInstance)
.map(ClientRequestObservationContext.class::cast)
.ifPresent(context -> context.setError(exception));
throw exception;
}
}
But observationRegistry#getCurrentObservation returns parent Observation (in my case http.server.requests
).
We use spring-web 6.0.14.
I think there should be some way for customization.
Comment From: bclozel
I'm not sure I understand the goal of this customization. You can throw RestClientException
instances from the error handler and they will be recorded as errors in the observation. As for why you can't get the observation as current in the error handler, this is because we didn't open a scope for the processing of the request; this could be useful if developers want to log custom error messages in their error handler and expect trace ids to be present.
Before repurposing this issue in that direction, I would like to get your feedback about throwing RestClientException
instead. Thanks!
Comment From: Vsevolod123
Unfortunately, it's very complicated to redesign 200+ applications in that way.
In fact, I've already been able to achive that what I need with ObservationHandler like this
public class RestTemplateObservationHandler implements ObservationHandler<ClientRequestObservationContext> {
private static final ThreadLocal<ClientRequestObservationContext> CONTEXT_THREAD_LOCAL = new ThreadLocal<>();
public static Optional<ClientRequestObservationContext> getCurrentContext() {
return Optional.ofNullable(CONTEXT_THREAD_LOCAL.get());
}
@Override
public void onStart(@NonNull ClientRequestObservationContext context) {
CONTEXT_THREAD_LOCAL.set(context);
}
@Override
public void onStop(@NonNull ClientRequestObservationContext context) {
CONTEXT_THREAD_LOCAL.remove();
}
@Override
public boolean supportsContext(@NonNull Observation.Context context) {
return context instanceof ClientRequestObservationContext;
}
}
and RestTemplateCustomizer (from spring-boot) like this ```java @Bean RestTemplateCustomizer addObservationErrorHandlerRestTemplateBuilderCustomizer() { return restTemplate -> { final var originalErrorHandler = restTemplate.getErrorHandler(); final var observationErrorHandler = new ResponseErrorHandler() { @Override public boolean hasError(@NonNull ClientHttpResponse response) throws IOException { return originalErrorHandler.hasError(response); }
@Override
public void handleError(@NonNull ClientHttpResponse response) throws IOException {
try {
originalErrorHandler.handleError(response);
} catch (Exception e) {
RestTemplateObservationHandler.getCurrentContext()
.ifPresent(context -> context.setError(e));
throw e;
}
}
};
restTemplate.setErrorHandler(observationErrorHandler);
};
} ```
But it seems strange that WebClient handles all exception types while RestTemplate handles only RestClientException.
I don't even know if it's worth closing the task.
Comment From: bclozel
So you mean that if all exceptions would be recorded as errors by the observations this would solve the issue for you? I thought the main problem was about getting the current observation.
Comment From: Vsevolod123
Yes, you are right. I think that problem with currentObservation is on the micrometer side.
Comment From: bclozel
No, that's not what I said.
Currently the instrumentation in RestTemplate
records only IOException
and RestClientException
exception as errors in the observation. This is because the actual HTTP exchange can only throw such exceptions. This would be a bit artificial, but we could instead record all Throwable
as errors - considering that error handlers throwing another type of exception should also be recorded as an error. Would this solve the problem for you?
Comment From: Vsevolod123
Custom ResponseErrorHandler could throw any Throwable
.
Yes, this will solve problem for me.