There is some magic code in the dispatcher which turns exceptions thrown and not caught in a spring boot 3.4 rest api (using @RestController and @GetMapping("/xx") into nice JSON responses & doing the logging:
- formats the error into a json object
- assigns a http status code depending on the type of the exception
- logs the exception. (MDC context not available here as is set (and removed) in a filter )
We need to customize the logging, to include correlation token which is set in the request MDC contact via a filter, which is not available to the context. We cant extend or change the way dispatcher does it, we have to start from scratch and try to replicate it.
We could do something like this:
@ControllerAdvice
public class ControllerConfig {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneralException(Exception ex) {
String jsonBody = turnTheExceptionIntoJson(ex);
HttpStatus httpStatus = figureOutTheStatus(3)
log.error("Some controller through an error", ex); // This will use our custom logback which includes correlation token in the MDC context for this request
return new ResponseEntity<>(jsonBody, httpStatus);
}
}
However, we need to replicate what the dispatcher currently outputs (see below), and how it maps exceptions to status codes, so we can maintain compatibility with existing clients.
Any ideas how to do this (e.g. where the source code for the dispatcher function which does this is so we can copy it). Especially how it gets the controller path, and how it maps statuses.
It will also have to handle Custom defined exceptions with @ResponseStatus(HttpStatus.xxx)
This is what the dispatcher does automatically:
{
"timestamp": "2025-02-14T13:30:59.422+00:00",
"status": 500,
"error": "Internal Server Error",
"trace": "net.snowflake.client.jdbc.SnowflakeSQLException: JWT token is invalid. [59166e77-2ff6-4c90-80e5-7d40b3121297]\r\n\tat net.snowflake.client.core.SessionUtil.newSession(SessionUtil.java:577)\r\n\tat......"
"message": "JWT token is invalid. [59166e77-2ff6-4c90-80e5-7d40b3121297]",
"path": "/my/controller"
}
We tried using @ExceptionHandler:
ControllerAdvice
public class ControllerConfig {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponseModel> handleGeneralException(Exception ex) {
// Return a custom error message with HTTP 500 status
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
ErrorResponseModel response = new ErrorResponseModel();
response.setTimetamp(new Date());
response.setMessage(ex.toString() + ":" +ex.getMessage());
response.setError(status.getReasonPhrase());
response.setStatus(status.value());
return new ResponseEntity<>(response, status);
}
}
The crux is this returns 500 error for every exception, including resource not found problems where the dispatchers built in system would have returned 404. If someone knows where the source code is for the dispatchers handling of exceptions, we could perhaps reverse engineer it, including logic to figure out the required status, and how it knows the path.