简体   繁体   中英

Spring MVC not logging all exceptions

I have Spring MVC setup to log exceptions using commons logging, but find that some runtime exceptions aren't being logged.

Here's my bean configuration for the default exception resolver provided by spring:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.Exception">error</prop>
        </props>
    </property>
</bean>

To get this to log most exceptions, I had to add the following line to my config:

    <property name="warnLogCategory" value="someCategoryStringYouMakeUp" />

So ultimately it became the following:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="warnLogCategory" value="apperror" />
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.Exception">error</prop>
        </props>
    </property>
</bean>

warnLogCategory is described in detail here .

To augment what @Brad Parks said I recommend that you also make a HttpServlet Filter that will run first (and finish last) to catch exceptions that Spring will miss. Also because there is Spring code that can fail like Spring Security and custom Http Interceptors that fall outside of the SimpleMappingException Resolver (ie if the DispatchServlet or Interceptors fail you will not get the exception).

Here's an example filter I use on top of the SimpleMappingExceptionResolver.

public class ExceptionLoggerServletFilter implements Filter {

    private CommonAccessLogFormatter requestFormat = new CommonAccessLogFormatter();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        try {
            chain.doFilter(request, response);
        } catch (Exception e) {
            StringBuilder sb = new StringBuilder("Very Bad shit has happened:");
            if (request instanceof HttpServletRequest)
                requestFormat.appendLogEntry((HttpServletRequest) request, sb);
            log.fatal(sb.toString(), e);
            throw new ServletException(e);
        }
    }

    @Override
    public void destroy() {
    }


    private static final Logger log = Logger.getLogger(ExceptionLoggerServletFilter.class);

}

The reason I recommend this is that for most servlet containers uncaught exception may or may not be logged with probably not your logging framework (for Tomcat its JULI).

The commons log access formatter just logs the request similar to Apache log files.

In my test, use SimpleMappingExceptionResolver can't log MissingServletRequestParameterException , I combined @ControllerAdvice and Filter to do log.

Use ControllerAdvice to catch Throwable raised in Spring MVC Controller.

@ControllerAdvice
public class GlobalDefaultExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger("global_controller_exception_logger");

    @ExceptionHandler(value = Throwable.class)
    public void defaultErrorHandler(Throwable e) throws Throwable {
        // If the exception is annotated with @ResponseStatus rethrow it and let
        // the framework handle it.
        // AnnotationUtils is a Spring Framework utility class.
        if (AnnotationUtils.findAnnotation
                (e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }

        // Otherwise log exception
        logger.error("global controller default exception handler", e);
        throw e;
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public void httpBadRequest(Exception e, HttpServletRequest request) throws Exception {
        StringBuffer requestURL = request.getRequestURL();
        logger.warn("{}   HTTP Status 400 - {}", requestURL, e.getMessage());
        throw e;
    }
}

Use Filter to catch additional Exception.

@WebFilter(
        filterName = "ExceptionLogFilter",
        urlPatterns = "/*",
        dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR}
)
public class ExceptionLogFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger("global_filter_exception_logger");


    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            chain.doFilter(request, response);
        } catch (IOException | ServletException e) {
            logger.error("bad thing happened during doFilter", e);
            throw e;
        }
    }

    ......
}

logback configuration

<logger name="global_controller_exception_logger" level="info"/>

<logger name="global_filter_exception_logger" level="info"/>

You can view my gist for full code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM