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();
}
网友评论