java - 在Spring Boot中的异常处理期间保留自定义MDC属性

标签 java spring spring-boot error-handling mdc

短版(具有足够的详细信息)

如何保留在doFilter()实现的javax.servlet.Filter方法中添加的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
    }
}

...在异常处理期间,如果在另一个过滤器或Controller(对chain.doFilter(...)的调用)中发生异常,则为...。

当前,如果发生异常:将执行finally块,清除MDC,然后将异常从过滤器中抛出。异常处理期间的所有日志将不包含MDC属性。

长版(过于详细)

我有一个简单的Filter实现来拦截所有请求。它仅创建随机字符串( token )以包含在与处理请求有关的所有日志中。
@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(),将随机 token 添加到MDC。
  • 通过调用chain.doFilter()处理请求。
  • 无论发生什么情况(处理完成,发生错误),都会清除MDC中的finally块中的随机 token 。

  • 问题是,如果发生错误并且已将其处理(例如,通过自定义的ErrorController实现),则相关日志不包含 token :
    [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),这意味着相关日志中不再有 token 。

    如何从接收请求直到发送响应(包括错误处理)到与请求的整个处理相关的所有日志中添加自定义标记? 我希望在线程发送响应后清除MDC,作为最后一个操作。无论发生什么情况(成功的响应,在错误处理期间引发异常等),都应清除MDC。

    当多个客户端同时使用Rest服务时,日志可能会变得很困惑。在特定请求的整个处理过程中,将唯一的 token 附加到每个日志中会极大地简化调试。

    最佳答案

    清除 ServletRequestListener#requestDestroyed() 而不是Filter上的MDC似乎效果很好。
    (Here是具体示例。)

    关于java - 在Spring Boot中的异常处理期间保留自定义MDC属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55146885/

    相关文章:

    java - 让线程无限期等待

    java - 缓存网络服务

    java - Spring jdbcTemplate 将常量值传递给查询方法

    java - 方法结束后丢失已确定的实例/null (@Autowire)

    java - 持久订阅者如何与 JMS 配合使用?

    spring-boot - 配置为监听端口 28081 的 Tomcat 连接器在 linux 中启动失败

    java - 保留表情符号,Java Regex

    java - 您可以自定义 IntelliJ 如何在方法声明和方法调用上自动设置换行格式吗?

    java - 将 JSON 发布到 Spring MVC Controller 返回 400 错误请求

    java - 由 : java.net.ConnectException : Connection refused: no further information: localhost/127. 0.0.1:9300 引起