【问题标题】:Spring MVC not logging all exceptionsSpring MVC 没有记录所有异常
【发布时间】:2012-07-26 10:00:46
【问题描述】:

我有 Spring MVC 设置来使用公共日志记录异常,但发现一些运行时异常没有被记录。

这是我对 spring 提供的默认异常解析器的 bean 配置:

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

【问题讨论】:

    标签: java spring exception logging spring-mvc


    【解决方案1】:

    为了让它记录大多数异常,我必须在我的配置中添加以下行:

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

    所以最终它变成了以下内容:

    <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 是 described in detail here

    【讨论】:

    • 那么使用这种方法会遗漏哪些异常,有没有什么办法可以得到呢?
    • 好点。好久没用Spring了,不记得了。我应该在上面包含更多信息。在这一点上,我最好的猜测是源自 RuntimeException 的异常,正如我在上面所暗示的那样。
    【解决方案2】:

    为了补充@Brad Parks 所说的内容,我建议您还创建一个 HttpServlet 过滤器,该过滤器将首先运行(最后完成)以捕获 Spring 将错过的异常。还因为有可能失败的 Spring 代码,例如 Spring Security 和 SimpleMappingException Resolver 之外的自定义 Http 拦截器(即,如果 DispatchServlet 或拦截器失败,您将不会收到异常)。

    这是我在 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);
    
    }
    

    我建议这样做的原因是,对于大多数 servlet 容器,未捕获的异常可能会或可能不会使用您的日志框架(对于 Tomcat 其 JULI)进行记录。

    commons 日志访问格式化程序只记录类似于 Apache 日志文件的请求。

    【讨论】:

    • 完全同意这个答案 - Spring 有一个严重的限制 - 它可以处理 only 实例异常类 ,所有Error 实例被盲目忽略 并发送到标准处理堆栈。因此,为了克服这个 Spring 缺陷,应该在整个堆栈顶部添加过滤器,并确保 ALL 异常得到处理跨度>
    【解决方案3】:

    在我的测试中,使用SimpleMappingExceptionResolver无法登录MissingServletRequestParameterException,我结合@ControllerAdviceFilter进行登录。

    使用ControllerAdvice 捕获 Spring MVC 控制器中引发的 Throwable。

    @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;
        }
    }
    

    使用Filter 捕获额外的异常。

    @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 配置

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

    您可以查看my gist获取完整代码。

    【讨论】:

      猜你喜欢
      • 2011-06-04
      • 2021-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-13
      • 2018-10-09
      • 1970-01-01
      • 2022-10-13
      相关资源
      最近更新 更多