微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 Logback MDC 记录 Spring Boot ErrorController 关于相关问题的回答

如何解决使用 Logback MDC 记录 Spring Boot ErrorController 关于相关问题的回答

(更新:我的问题似乎与 this one 相同,但没有有效答案。)

我正在尝试登录 Spring Boot ErrorController,但它的日志没有 MDC 值。

@Controller
@RequestMapping("/error")
@requiredArgsConstructor
@Slf4j
public class MyErrorController implements ErrorController {

    private final ErrorAttributes errorAttributes;

    @Override
    public String getErrorPath() {
        return null;
    }

    @RequestMapping
    @ResponseBody
    public Map<String,String> error(final HttpServletRequest request) {
        final ServletWebRequest webRequest = new ServletWebRequest(request);
        final Throwable th = errorAttributes.getError(webRequest);

        if (th != null) {
            // **Logged without "requestId" value**
            log.error("MyErrorController",th);
        }

        return Map.of("result","error");
    }
}
// http://logback.qos.ch/manual/mdc.html#autoMDC
public class MDCFilter extends OncePerRequestFilter {

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

        final String requestId = UUID.randomUUID().toString();

        MDC.put("requestId",requestId);

        try {
            filterChain.doFilter(request,response);
        } finally {
            MDC.remove("requestId");
        }
    }

}
@Configuration
public class MyConfig {

    @Bean
    public FilterRegistrationBean<MDCFilter> mdcFilter() {
        final FilterRegistrationBean<MDCFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new MDCFilter());
        bean.addUrlPatterns("/*");
        bean.setorder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

logback-spring.xml

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] requestId:%X{requestId} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

result(requestId 值没有出现):

18:15:13.705 [http-nio-8080-exec-1] requestId: ERROR c.e.l.MyErrorController - MyErrorController
java.lang.RuntimeException: Error occured.
...

Here is complete code

我认为我需要在MDCFilter之前适应dispatcherServlet,但我不知道该怎么做。

解决方法

一切都按预期进行。

这是记录的行:

08:19:34.204 [http-nio-8080-exec-2] MY_MDC_VALUE DEBUG o.s.web.servlet.DispatcherServlet - Failed to complete request: java.lang.RuntimeException: Error occured.

您发布的是来自 MyErroController 的日志

08:19:34.209 [http-nio-8080-exec-2]  ERROR c.e.l.MyErrorController - MyErrorController

当你打电话时

MDC.clear();

执行此日志语句时,MDC 为空。

,

删除 ServletRequestListener#requestDestroyed() 上的 MDC 数据,而不是 Filter 上的数据。

在 Tomcat 上 StandardHostValveRequestDestroyEvent 执行后触发 ErrorController

            // Look for (and render if found) an application level error page
            if (response.isErrorReportRequired()) {
                // If an error has occurred that prevents further I/O,don't waste time
                // producing an error report that will never be read
                AtomicBoolean result = new AtomicBoolean(false);
                response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED,result);
                if (result.get()) {
                    if (t != null) {
                        throwable(request,response,t); // *ErrorController is executed*
                    } else {
                        status(request,response);
                    }
                }
            }

            if (!request.isAsync() && !asyncAtStart) {
                context.fireRequestDestroyEvent(request.getRequest()); // *RequestDestroyEvent is fired*
            }

因此,请执行以下操作:

public class MDCClearListener implements ServletRequestListener {

    @Override
    public void requestDestroyed(final ServletRequestEvent sre) {
        MDC.remove("requestId");
    }
}
    @Bean
    public ServletListenerRegistrationBean<MDCClearListener> mdcClearListener() {
        final ServletListenerRegistrationBean<MDCClearListener> bean = new ServletListenerRegistrationBean<>();
        bean.setListener(new MDCClearListener());
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }

(具体代码存在于 solution branch。)


关于相关问题的回答

This answer 不适合我。因为:

第一种方式不使用ErrorController而是使用@ExceptionHandler,因此无法捕获Spring Security Filter抛出的异常。 (尝试 answer/exceptionhandler-with-springsecurity branch 代码。)

第二种方式将 UUID 放在拦截器上,因此记录了 MyControllerMyErrorController 之间的不同 requestId。这不是“请求”ID。 (尝试 answer/interceptor branch 代码。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?