简短版本(含足够细节)
如何保留在javax.servlet.Filter
实现的doFilter()
方法中添加的MDC属性...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
MDC.put("token", MyToken.random()); // add the MDC attribute related to the current request processing
chain.doFilter(request, response); // send the request to other filters and the Controller
} finally {
MDC.clear(); // MDC attribute must be removed so future tasks executed on the same thread would not log invalid information
}
}
如果在另一个过滤器或控制器(对chain.doFilter(...)
的调用)中发生异常,那么在异常处理期间将清除MDC,并且异常将从过滤器中抛出。当前,如果出现异常:finally块将被执行以清除MDC,然后异常将被抛出筛选器之外。在异常处理期间的所有日志都不会包含MDC属性。
详细版本
我有一个简单的Filter
实现来拦截所有请求。它只创建一串随机字符(令牌),以便包含在与处理请求相关的所有日志中。
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
MDC.put("token", MyToken.random());
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
@Override
public void destroy() {
}
}
事件的顺序将会是:
- 请求被接收。
- 我的
doFilter()
被调用,将随机令牌添加到 MDC 中。 - 通过调用
chain.doFilter()
来处理请求。 - 无论发生什么 (处理完成、出现错误),MDC 中的随机令牌都会在
finally
块中被清除。
问题在于,如果发生了错误并且被处理(例如由自定义 ErrorController
实现处理),相关的日志不包括令牌:
[2019.03.13 15:00:14.535] token:308...8bf [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet : GET "/resource", parameters={}
[2019.03.13 15:00:14.551] token:308...8bf [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet : Completed 400 BAD_REQUEST
[2019.03.13 15:00:14.551] token: [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet : "ERROR" dispatch for GET "/error", parameters={}
[2019.03.13 15:00:14.551] token: [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.springframework.http.ResponseEntity myproj.CustomErrorController.handleError(javax.servlet.http.HttpServletRequest)
[2019.03.13 15:00:14.551] token: [ERROR] 8124 [https-jsse-nio-8443-exec-7] m.CustomErrorController : HTTP Error: Bad Request (400)
[2019.03.13 15:00:14.551] token: [DEBUG] 8124 [https-jsse-nio-8443-exec-7] o.s.w.s.DispatcherServlet : Exiting from "ERROR" dispatch, status 400
finally
块在由处理它的 Controller
抛出异常时执行,导致清除 MDC。此后执行错误处理(包括自定义的
ErrorController
),这意味着相关日志中没有更多的令牌。
我该如何将自定义标记添加到与请求的整个处理过程(包括错误处理)相关的所有日志中,从接收请求到发送响应为止? 我想要在线程发送响应之后清除 MDC,作为最后一步操作。无论发生什么情况(成功响应、在错误处理期间抛出异常等),MDC 都应该被清除。使用 Rest 服务的多个客户端同时访问,日志可能会变得非常混乱。将唯一标识符附加到某个请求的整个处理过程中产生的每个日志记录中将极大地简化调试。