查找日志的时候大家可能会有感受,由于日志打印一般是无序的,多线程下想要拿到一次请求中的相关日志简直是大海捞针。亦或者多系统间相互调用的时候如何快速找到某次请求的链路,MDC的出现就是解决当前问题很好的选择。
SLF4J的MDC
SLF4J 提供了MDC ( Mapped Diagnostic Contexts )功能,它的实现也是利用了 ThreadLocal 机制。 在代码中,只需要将指定的值 put 到线程上下文的 Map 中,然后在对应的地方使用 get 方法获取对应的值,从而达到自定义和修改日志输出格式内容的目的。目前只有log4j和logback提供原生的MDC支持。
应用方法
- 创建Interceptor或者filter。拦截所有请求,在处理请求前将TRACE_ID放到MDC中,在处理完请求后清除MDC的内容。
public class LoggingInterceptor implements HandlerInterceptor {
public static final String PROJECT_NAME = "PANACEA-";
private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
MDC.put(MDCKeys.REQUEST_URI, request.getRequestURI());
MDC.put(MDCKeys.REQUEST_METHOD, request.getMethod());
MDC.put(MDCKeys.TRACE_ID, TraceIdGenerator.nextTraceId(PROJECT_NAME));
logger.info("LoggingInterceptor.preHandle,request:",request);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("LoggingInterceptor.afterCompletion");
MDC.clear();
}
}
- 注册Interceptor
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor()).addPathPatterns("/**");
}
}
- 设置日志输出格式,日志文件添加对应的MDC变量值
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%n [%-5level][%d{yyyy-MM-dd HH:mm:ss.SSS}]
*******[%mdc{TRACE_ID}] [%thread] [%mdc{REQUEST_URI}] *****
%msg [%file:%line] %logger{35}%n</pattern>
</layout>
</appender>
以上!
网友评论