How to include url in default spring servlet exception logger - spring

When an exception occurs, a message is logged as:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is <the real exception>
How can I modify this message to include the request url that caused the exception?

It seems that this log error is shown only if the exception is not resolved by spring, so it propagates up to tomcat's StandardWrapperValve.java that logs the error:
catch (ServletException e) {
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceExceptionRoot",
wrapper.getName(), context.getName(), e.getMessage()),
rootCause);
}
throwable = e;
exception(request, response, e);
}
I have no control over tomcat's logged message so I've created a fallback spring HandlerExceptionResolver that replaces the last exception resolver DefaultHandlerExceptionResolver, preventing the exception to propagate to tomcat:
#Configuration
public class WebConfiguration implements WebMvcConfigurer {
#Override
public void extendHandlerExceptionResolvers( List<HandlerExceptionResolver> resolvers ) {
resolvers.stream()
.filter( r -> r instanceof DefaultHandlerExceptionResolver ).findAny()
.ifPresent( resolvers::remove );
resolvers.add( new FallbackHandlerExceptionResolver() );
}
private static class FallbackHandlerExceptionResolver extends DefaultHandlerExceptionResolver {
private static final Logger log = LoggerFactory.getLogger( FallbackHandlerExceptionResolver.class );
#Override
public ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex ) {
ModelAndView mv = super.resolveException( request, response, handler, ex );
if (mv == null) {
try {
log.error( String.format( "Error %s %s", request.getMethod(), UrlUtils.getRequestUrl( request ) ), ex );
response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
return new ModelAndView(); //skip next handlers
} catch (IOException e) {
log.error( "Error sending response error", e );
}
}
return mv;
}
}
}

Related

how to configure 500 error page with Spring boot 2.5.2, the default set up not working

I am relying on spring boot to handle the 500 (Internal server error). Followed various links which mentioned that I could customize my web app using the below properties
server.error.path=/error
server.error.whitelabel.enabled=false
I also wrote my custom error page controller as:
#Controller
public class GlobalErrorController implements ErrorController {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
log.error("Error occurred!");
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
// handle all the 400 error codes
if (statusCode != null && statusCode % 400 < 100) {
return "error/4xx";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "error/500";
}
}
return "error/genericError";
}
}
This controller is never being called and I only see the server error page with console errors as below:
I can see see the error is clearly being thrown with /error page in console. Below is part of the error log:
2021-07-27 16:13:28.769 ERROR 22040 --- [nio-8080-exec-1]
o.a.c.c.C.[.[localhost] 175 : Exception Processing
ErrorPage[errorCode=0, location=/error]
One thing to note here is that the exception is being thrown in the filter, because I was testing the behavior of the app when one of the service goes down. But I should have still gotten the page I was expecting with /error controller.
Update:
I did some further research and found that an exception thrown in filters would not be handled by global exception handler. In order to handle the exceptions by spring's exception handler I added this filter which catches the exceptions raised by any other filters. The code is below:
#Component
public class ExceptionHandlerFilter extends OncePerRequestFilter {
private final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
#Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (RuntimeException ex) {
log.error("Spring Security Filter Chain Exception:", ex);
resolver.resolveException(request, response, null, ex);
}
}
}
I also created a exception handler just for this as below:
#ControllerAdvice
public class GlobalExceptionHandler {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleError(HttpServletRequest req, RuntimeException ex)
{
log.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView("error/500");
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
return mav;
}
}
Now the issue is that it just shows the blank page without any content. I was expecting to see my custom error page located at error/500.
This code works fine for me. Could you change your class to below code and tell the result?
error_404 and error_500 is custom html error pages.
#Controller
public class MyErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
int statusCode = Integer.parseInt(status.toString());
switch (statusCode) {
case 404:
return "error_404";
case 500:
return "error_500";
}
}
return "error";
}
#Override
public String getErrorPath() {
return null;
}
}
server.error.whitelabel.enabled=false
spring.mvc.throw-exception-if-no-handler-found=true
server.error.path=/error

Spring Boot ControllerAdvice TimeoutException Not Caught

I have multiple ExceptionHandler in controllerAdvice. one for TimeoutException and other for Exception classes.
When i throw new TimeoutException, it gets caught in Exception Handler not in TimeoutException Handler
below is the code:
#ControllerAdvice
public class CoreControllerAdvice {
#ExceptionHandler(value = { Exception.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
log.info("handleException", ex);
}
#ExceptionHandler(value = { TimeoutException.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleTimeoutException(Exception ex) {
log.info("handleTimeoutException", ex);
}
}
I throw Exception as
throw new TimeoutException("test");
Can some one help, why it is not caught by TimeoutException Handler
Thanks,
The parameter in the method handleTimeoutException seems incorrect to me, instead of Exception it should be TimeoutException.
#ExceptionHandler(value = { TimeoutException.class })
#ResponseBody
public ResponseEntity<ErrorResponse> handleTimeoutException(TimeoutException ex) {
log.info("handleTimeoutException", ex);
}

Spring ConstraintViolation not caught

I'm using Spring Boot, and I have created a custom PathVariable validator, which I use as follows:
#GetMapping(value = "/test/{id}")
public ResponseEntity<Void> test(#PathVariable("id")
#Valid
#MyGUID(message = "ID_INVALID")
String id) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
The validor seems to do its job well - it passes when input is valid, and throws exception when not. The problem is, that my custom exception handler doesn't catch the ConstraintViolationException that is thrown. This is the top line of the stack trace:
javax.validation.ConstraintViolationException: ID_INVALID
Then I have my exeception handler class:
#ControllerAdvice
public class RestExceptionController extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
List<String> errorDetailsCodes = new ArrayList<>();
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
errorDetailsCodes.add(violation.getMessage());
}
ErrorResponse errorResponse = new ErrorResponse(
status.name(),
status.value(),
ex.hashCode(),
errorDetailsCodes
);
logException(ex);
return new ResponseEntity<>(errorResponse, headers, status);
}
#ExceptionHandler(value = Exception.class)
public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.name(),
HttpStatus.INTERNAL_SERVER_ERROR.value(),
ex.hashCode()
);
logException(ex);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
It is strange to me that despite the fact I have both a specific and a general exception handler, they are both skipped, and the system throws its own error:
{
"timestamp": 1568554746642,
"status": 500,
"error": "Internal Server Error",
"message": "ID_INVALID",
"path": "/api/logged-in-user/test/123"
}
How can I catch this exception and return my custom response?
Thanks.

Get exception object in custom error controller

I am using spring boot and write a global exception handler use AbstractErrorController. How could i get an exception object in controller?
#Controller
public class MyCustomErrorController extends AbstractErrorController {
public MyCustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
#RequestMapping("/error")
public void handleError(HttpServletRequest req, HttpServletResponse resp) {
Exception e = ...; // how to get exception here
log.error(e);
displayError(req, resp, e);
}
#Override
public String getErrorPath() {
return "/error";
}
}
You can get the exception from the HttpServletRequest as follows:
#Controller
public class MyCustomErrorController extends AbstractErrorController {
#RequestMapping("/error")
public void handleError(HttpServletRequest request) {
Exception e = (Exception) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
...
}
}
An handler intercepts an Exception generated or re-thrown by a controller. It doesn't have an endpoint because it usually does it for all the controllers in your application. The Handler instructs the application server to return a specific error when a specific Exception is thrown.
Here is an example:
#ControllerAdvice // Specialization of #Component for classes that declare #ExceptionHandler, #InitBinder, or #ModelAttribute methods to be shared across multiple #Controller classes.
public class ResourceNotFoundExceptionHandler {
#ExceptionHandler(value = { ResourceNotFoundException.class })
public ResponseEntity<Object> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ApiError error = new ApiError(HttpStatus.NOT_FOUND, ex.getLocalizedMessage(), ex);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
ResponseEntity<Object> response = new ResponseEntity<>(error, headers, HttpStatus.NOT_FOUND);
return response;
}
}
In this example ApiError is a data structure that reports the error to the UI. What this code does is intercepting the Exception "ResourceNotFoundException", create an appropriate error Data transfer object, set the response HttpStatus and headers and return the error.
you can find a different example here: https://github.com/otrebor/springbootseed-openshift/blob/master/src/main/java/com/company/example/springbootseed/core/errorhandling/handlers/
Add Exception as an extra parameter to handleError()

Setting the status code message of a HTTP ResponseCode thrown by an #ResponseCode annotate Exception

I am currently trying to set the message of a HTTP Status Code thrown by an #ResponseCode annotated Exception.
I have defined the exception:
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public final class BadRequestException extends IllegalArgumentException {
/**
* The serial version UID.
*/
private static final long serialVersionUID = -6121234578480284282L;
public BadRequestException() {
super();
}
public BadRequestException(String message, Throwable cause) {
super(message, cause);
}
public BadRequestException(String message) {
super(message);
}
public BadRequestException(Throwable cause) {
super(cause);
}
}
If the exception is thrown I throw it again in my #ExceptionHandler annotate method:
#ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(Exception e, HttpSession session) {
if (e instanceof BadRequestException) {
throw (BadRequestException)e;
}
return FAILURE;
}
I generally throw this exception in this way:
if (result.hasErrors()) {
throw new BadRequestException("Bad Request Message.");
}
The HTTP Status Code always returns only "HTTP Status 400 -" without a message is set.
THX!
Annotate your exception handler with the #ResponseStatus. Then created a basic error/exception view and pass the exception stack trace or whatever to that view

Resources