1、MDC介绍
我们在使用日志框架输入日志的时候,常规情况,在一个请求中所输出的日志之间是没有关系的,即并不知道哪些日志是某一次请求而输出的。
为了能更好的能从请求维度追踪日志,我们需要在日志中添加”请求标识“。在slf4j日志框架中提供了MDC(Mapped Diagnostic Context,映射诊断上下文)机制来实现
org.slf4j.MDC
对象从org.slf4j.spi.SLF4JServiceProvider
中获取org.slf4j.spi.MDCAdapter
的实现类来完成操作.
1)BasicMDCAdapter
该实现类,时间put进来的数据存储到InheritableThreadLocal实例中,在get方法的时候,从该实例中返回数据。
2)LogbackMDCAdapter
和BasicMDCAdapter相识,只是将数据存储在了ThreadLocal中。
2、MDC使用
一般情况下,为了能从会话级别来跟踪日志,我们往往需要在某个地方将数据放入MDC中,常见有以下几种方式。
2.1 使用Filter
创建一个Filter,然后在Filter中给MDC添加会话级别数据,同时在请求处理完成后,清除MDC中的数据
public class MDCFilter implemtes Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException{
add2MDC(request);
try {
chain.doFilter(request, response);
} finally {
clearMDC();
}
}
private add2MDC(ServletRequest request) {
// TODO 从request中获取需要的标识数据,例如请求的URI、请求的HTTP方法、请求中的host、
// 自定义的traceId或requestId等
// 将定义的KEY和得到得value数据put进MDC中
MDC.put("KEY","") ;
}
private clearMDC() {
// TODO 将MDC中保存的数据清除掉
MDC.put("KEY","") ;
}
}
2.2 使用HandlerInterceptor
public class MDCHandlerInterceptor extends HandlerInterceptor {
@Override
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO 获取数据put进入MDC
return true;
}
@Override
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
// TODO 清空MDC中的数据
}
}
然后对拦截器进行注册
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
MDCHandlerInterceptor mcHandlerInterceptor = new MDCHandlerInterceptor();
registry.addInterceptor(mcHandlerInterceptor);
}
}
2.3 MDCInsertingServletFilter
如果使用的是logback,还可以使用其提供的ch.qos.logback.classic.helpers.MDCInsertingServletFilter
,将其注册到Servlet容器中即可。但需要注意的是,该Filter中添加的数据或许不能满意您的需求。
MDC key | MDC value |
req.remoteHost | as returned by the getRemoteHost() method |
req.xForwardedFor | value of the "X-Forwarded-For" header |
req.method | as returned by getMethod() method |
req.requestURI | as returned by getRequestURI() method |
req.requestURL | as returned by getRequestURL() method |
req.queryString | as returned by getQueryString() method |
req.userAgent | value of the "User-Agent" header |
随后我们只需要在日志输出格式配置文件中配置上MDC中对应的key即可。
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{requestId} - %msg%n</pattern>
</encoder>
</appender>
其中,%X{requestId}
就是用来输出MDC中key为"requestId"的值。