MDC(Mapped Diagnostic Context,映射调试上下文)是Slf4j(提供了接口定义和核心实现,日志库负责适配器的实现)提供的一种方便
在多线程条件下记录日志
的功能。
MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。
简而言之,MDC就是日志框架提供的一个InheritableThreadLocal,项目代码中可以将键值对放入其中,然后使用指定方式取出打印即可。
使用Servlet Filter在接收到请求时,生成UUID填充MDC。在logback的配置中就可以
使用
%X{key}
打印MDC中的内容
,从而识别出同一次请求中的log。
以下是一个简单的MDCFilter实现示例,在MDC中填充了UUID、主机IP和主机名。UUID用于标记同一次请求中的log,主机IP和主机名方便异常发生时到相应机器上检查log。
log4j 1.x中MDCFilter实现
//MDC填充的KEY
public static final String REQUEST_UUID = "REQUEST_UUID";
public static final String HOST_IP = "HOST_IP";
public static final String HOST_NAME = "HOST_NAME";
public static final String APPKEY = "APPKEY";
private String appkey = null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
appkey = filterConfig.getInitParameter("appkey");//appkey可以在filter init-param中配置
if(appkey == null) {
appkey = "";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
insertIntoMDC(request);//填充MDC
chain.doFilter(request, response);
} finally {
clearMDC();//请求结束,注意清除MDC中的内容,否则会造成内存泄露问题
// 默认实现中填充了UUID,主机IP和主机地址
protected void insertIntoMDC(ServletRequest request) {
MDC.put(MDCFilter.REQUEST_UUID, UUID.randomUUID().toString());
MDC.put(MDCFilter.HOST_IP, getHostIP());
MDC.put(MDCFilter.HOST_NAME, getHostName());
MDC.put(MDCFilter.APPKEY, appkey);
protected void clearMDC() {
MDC.remove(MDCFilter.REQUEST_UUID);
MDC.remove(MDCFilter.HOST_IP);
MDC.remove(MDCFilter.HOST_NAME);
MDC.remove(MDCFilter.APPKEY);
log4j2.0中MDCFilter实现
在log4j 2.0 中,使用ThreadContext代替了MDC和
NDC[http://logging.apache.org/log4j/2.x/manual/thread-context.html]。
log4j 2.x 中,类似MDCFilter的实现:ThreadContextFilter
public static final String REQUEST_UUID = "REQUEST_UUID";
public static final String HOST_IP = "HOST_IP";
public static final String HOST_NAME = "HOST_NAME";
public static final String APPKEY = "APPKEY";
private String appkey = null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
appkey = filterConfig.getInitParameter("appkey");//appkey可以在filter init-param中配置
if(appkey == null) {
appkey = "";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
insertIntoMDC(request);
chain.doFilter(request, response);
} finally {
clearMDC();
protected void insertIntoMDC(ServletRequest request) {
ThreadContext.put(ThreadContextFilter.REQUEST_UUID, UUID.randomUUID().toString());
ThreadContext.put(ThreadContextFilter.HOST_IP, getHostIP());
ThreadContext.put(ThreadContextFilter.HOST_NAME, getHostName());
ThreadContext.put(ThreadContextFilter.APPKEY, appkey);
protected void clearMDC() {
ThreadContext.remove(ThreadContextFilter.REQUEST_UUID);
ThreadContext.remove(ThreadContextFilter.HOST_IP);
ThreadContext.remove(ThreadContextFilter.HOST_NAME);
ThreadContext.remove(ThreadContextFilter.APPKEY);
logback配置中,使用MDC
在logback配置中,使用mdc:(log4j 1.x 与 2.x中都可以使用此配置)
%X{REQUEST_UUID} %X{HOST_IP} %X{HOST_NAME} %X{APPKEY}
MDC(Mapped Diagnostic Contexts)映射诊断上下文,该特征是logback提供的一种方便在多线程条件下的记录日志的功能。
某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线程池中复用已有的线程。在一个用户的会话存续期间,可能有多个线程处理过该用户的请求。这使得比较难以...
一、MDC介绍
MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中,可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也...
苏格拉底说过:日志打得好,排查没烦恼
我们日常的开发工作中,排查问题去看日志应该是家常便饭的事,日志可以帮助我们清楚的知道当前代码的走向以及链路数据,通常我们现在都是微服务的架构,那么在我们 A 系统调用到 B 系统中,B 系统又调用 C 系统
在繁忙的日志中,我们如何全链路追踪一笔调用呢,通常我们会借助到全局流水号这样的概念,例如下图这样
实现这...
LOG4J2的MDC应用
MDC的概念:Manufacturing Data Collection 生产数据实时采集和分析。
有时实际开发过程没有发现的BUG在生产环境才出现,需要到生产环境去分析实时日志来进行BUG跟踪;
LOG4J2提供了MDC功能可以将特定用户的日志单独处理输出到特定的文件中。
配置关键字ThreadContext ,DynamicThresholdFilter ,ThreadContextMapFilter
1.DynamicThresholdFilter 从整体上控制日志默认输出级别,对于特定的值可以调整日志级别
<!--ThreadContext.put("loginId", "User1"); 除了User1之外其他的日志都默认是ERROR级别,USER1相关的日志是DEBUG级别 -->
<DynamicThresholdFilter key="loginI
* This class hides and serves as a substitute for the underlying logging
* system's MDC implementation.
* If the underlying logging system offers MDC functionality,
日志的作用是什么?调试,问题定位,数据分析。日志很重要,要保证统一的样式,分级别,请求可追溯。
1. springboot 自带的logback
如果你是 ieda 开发工具,并且是maven工程,
可以点开 pom 文件,右键 -> maven -> Show Dependencies ,可以查看整个项目的依赖关系,其中有 logback
2.配置 logback.xml 文件
这个文件配置好放在 resources 目录下即可(不需要其他任何地方的配置),工程可自动识别,仔细阅读文
MDC是可以帮组我们 在多线程条件下记录追踪日志的功能,它支持 Log4J和LogBack 两种日志框架通常打印出的日志会有线程号等信息来标志当前日志属于哪个线程,然而由于线程是可以重复使用的,所以并不能很清晰的确认一个请求的日志范围。手动生成一个唯一序列号打印在日志中;使用日志控件提供的MDC功能,生成一个唯一序列标记一个线程的日志在现网出现故障时,我们经常需要获取一次请求流程里的所有日志进行定位。
一、什么是MDC?
MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能,也可以说是一种轻量级的日志跟踪工具。
二、MDC能为我们做什么?
当我们在开发过程中遇到问题时,我们大多数情况下都会借助查询日志寻找问题,那么如何在海量的日志中查询到我们某个请求的一系列调用信息便显得尤为重要,那么MDC就能为我们解决这一问题。
三、MDC衍生的具体场景
在开发过程中,大多数情况下我们的一个请求调用链路可能相当复杂,并
1.为什么最开始子线程会得到父线程MDC设置的内容?
创建子线程的时候会调用init(ThreadGroup g, Runnable target, String name,long stackSize)方法,判断如果parent.inheritableThreadLocals不为null就调用createInheritedMap方法把父线程的ThreadLocal里保存的变量都加载到子线程的T
因为MDC底层是用ThreadLocal实现的,所以这里补充一些和ThreadLocal相关的知识点。
1.ThreadLocal的三个层次
关于ThreadLocal有三个层次,可以按照这三个层次去理解就不会乱。
* 第一层是Thread空间
* 第二层是Thread中的两个ThreadLocalMap,threadLocals和inheritableThreadLoca