17

Does regular Spring (Boot) have a way to do global exception handling, or at least catch any uncaught exceptions (such as RuntimeException that may happen randomly)?

I've googled around, but everything I've found talked about the @ControllerAdvice @ExceptionHandler for controllers. Nothing about just a general global exception handler.

I basically just want to make sure that if some exception happens that I'm not already catching, that I log it so I know about it.

6
  • Reading this stackoverflow.com/a/28678037/3959856 makes it sound like @ControllerAdvice is the way. Why not? Commented Dec 11, 2017 at 14:29
  • There is also ErrorController docs.spring.io/spring-boot/docs/current/api/org/springframework/… which is onle level higher than ControllerAdvice or ExceptionHandler. But not sure if that is what you want Commented Dec 11, 2017 at 14:30
  • @JackFlamp I'm guessing OP is not using controllers, that's why this approach doesn't work Commented Dec 11, 2017 at 14:31
  • Why not using Thread.UncaughtExceptionHandler? Commented Dec 11, 2017 at 14:37
  • 1
    @JackFlamp As Lino mentioned, for this I'm not using controllers. My particular case is actually using @Scheduled to trigger tasks automatically. Commented Dec 11, 2017 at 15:12

3 Answers 3

4

I think spring does not provide an out of the box solution for this.

A possibility to achieve this is using AOP with annotations (you may find an example here: Where can I catch non rest controller exceptions in spring?). With this approach, you can define a global pointcut for all the methods annotated with a pre-defined annotation. The good part of this approach is to have all the error handling logic in one place.

Sign up to request clarification or add additional context in comments.

Comments

2

Add your own via AOP, something like this will work:

@Aspect
@Component
public class ExceptionHandler {

    @AfterThrowing(pointcut = "within(*.*)", throwing = "t")
    public void log(Throwable t) {
        t.printStackTrace();
    }
}

Look at the cheat sheet to help with the pointcut.

2 Comments

Just like the OP, I have a Spring project that doesn't serve endpoints. I tried to add this to my Thread.UncaughtExceptionHandler but it didn't work.
Although I actually got a formal unbound in pointcut, which means this may still work if I can fix this aspect thing.
-2

Best to extend Spring's ResponseEntityExceptionHandler class and customise it in a way you want, for example:

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.validation.ValidationException;

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(ValidationException.class)
    protected ResponseEntity<Object> handle(ValidationException ex) {
        return new ResponseEntity<>(getError(HttpStatus.BAD_REQUEST, ex), new HttpHeaders(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler
    protected ResponseEntity<Object> handle(Exception ex, WebRequest request) {
        try {
            return super.handleException(ex, request);
        } catch (Exception e) {
            return handleExceptionInternal(ex, null, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
        }
    }

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (HttpStatus.Series.SERVER_ERROR == status.series()) {
            log.error("Unexpected error.", ex);
        }
        return new ResponseEntity<>(getError(status, ex), headers, status);
    }

    private ApiError getError(HttpStatus status, Exception ex) {
        return ApiError.builder()
            .status(status.value())
            .message(ex.getMessage())
            .build();
    }
}

This way you can override Spring's default error response body, declare your custom exceptions (e.g. ValidationException) as well as allow Spring to handle its default exceptions declared in ResponseEntityExceptionHandler.

Note although try-catch around super.handleException(ex, request) is not required in Spring 4.x, it is necessary in Spring 5.x due to a change of the underlying ResponseEntityExceptionHandler implementation.

1 Comment

OP asked for a non MVC solution

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.