Filter级别统一异常处理(异常记录日志)

1.介绍

项目中要做一个转发系统(gateway),转发到的第三方系统可能是web service或者http。采取在filter级别做流向控制,web service的请求由系统内部自己处理,用的apache cxf框架,http的请求由zuul做转发处理。为了记录每一次请求的详情并记录入库,如入参、出参、头信息、请求用户和异常等,在刚进入最外层的filter时创建log对象,并放到ThreadLocal中,在doFilter之后将日志记录入库。

2.遇到的问题

在使用中,发现一旦遇到不可预知的异常,线程终止,程序无法走到记录日志入库那一步,导致记录缺失。

3.解决方案

参考Spring框架中日志记录的案例,在org.springframework.web.filter.AbstractRequestLoggingFilter中有体现。

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    boolean isFirstRequest = !isAsyncDispatch(request);
    HttpServletRequest requestToUse = request;

    if (isIncludePayload() && isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
        requestToUse = new ContentCachingRequestWrapper(request, getMaxPayloadLength());
    }

    boolean shouldLog = shouldLog(requestToUse);
    if (shouldLog && isFirstRequest) {
        beforeRequest(requestToUse, getBeforeMessage(requestToUse));
    }
    try {
        filterChain.doFilter(requestToUse, response);
    }
    finally {
        if (shouldLog && !isAsyncStarted(requestToUse)) {
            afterRequest(requestToUse, getAfterMessage(requestToUse));
        }
    }
}

可以看到,此处用try finally进行了巧妙的处理,不管doFilter里面发生了什么,都进行日志处理。
最终我们仿照这段代码,解决了问题。代码如下:

Throwable myThrowable = null;
try {
    filterChain.doFilter(servletRequest, servletResponse);
} catch (Throwable throwable) {
    // 记录外抛异常
    myThrowable = throwable;
    throw throwable;
} finally {
    // 记录在doFilter里被程序处理过后的异常,可参考 http://www.runoob.com/servlet/servlet-exception-handling.html
    Throwable throwable = (Throwable) httpRequest.getAttribute("javax.servlet.error.exception");
    if (throwable != null) {
        myThrowable = throwable;
    }
    //日志记录
    LogUtils.postLog(myThrowable);
    //处理ThreadLocal
    ThreadLocals.removeAll();
}

你可能感兴趣的:(Filter级别统一异常处理(异常记录日志))