1、MDC介绍

我们在使用日志框架输入日志的时候,常规情况,在一个请求中所输出的日志之间是没有关系的,即并不知道哪些日志是某一次请求而输出的。

为了能更好的能从请求维度追踪日志,我们需要在日志中添加”请求标识“。在slf4j日志框架中提供了MDC(Mapped Diagnostic Context,映射诊断上下文)机制来实现

slf4j的MDC_ide

org.slf4j.MDC对象从org.slf4j.spi.SLF4JServiceProvider中获取org.slf4j.spi.MDCAdapter的实现类来完成操作.

slf4j的MDC_ide_02

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"的值。

slf4j的MDC_数据_03