MDC是什么?用法、源码一锅端
近期用到阿里的一款开源的数据同步工具 Canal,不经意之中看到了 MDC 的用法,而且平时项目中也多次用到 MDC,趁机科普一把。
通过今天的分享,能让你轻松 get 如下几点,绝对收获满满。
a)MDC 快速入门;
b)MDC 源码解读;
c)MDC 能干什么?
阿里开源项目 Canal:
老项目这么用过:
但是无论怎么用,都逃不过 MDC API 的使用,下面先花一分钟快速入门,然后再逐步去深入 MDC。
1. MDC 快速入门
MDC 全称是 Mapped Diagnostic Context,可以粗略的理解成是一个线程安全的存放诊断日志的容器。
首先看看 MDC 基本的 API 的用法,能抛代码的就不多废话(根据 logback 官方案例改编)。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;
* MDC快速入门示例
* @author 程序要老王头
public class SimpleMDC {
private static final Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
public static final String REQ_ID = "REQ_ID";
public static void main(String[] args) {
MDC.put(REQ_ID, UUID.randomUUID().toString());
logger.info("开始调用服务A,进行业务处理");
logger.info("业务处理完毕,可以释放空间了,避免内存泄露");
MDC.remove(REQ_ID);
logger.info("REQ_ID 还有吗?{}", MDC.get(REQ_ID) != null);
代码编写完,貌似只有 MDC.put(K,V) 、MDC.remove(K) 两句是陌生的,先不着急解释它,等案例跑完就懂了,咱们继续往下看。
接下来配置 logback.xml,通过 %X{REQ_ID} 来打印 REQ_ID 的信息,logback.xml 文件内容如下。
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>[%t] [%X{REQ_ID}] - %m%n</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
引入依赖包,让代码跑起来
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
程序跑起来,输出截图如下。
根据输出结果分析,能够得到两条结论。
第一: 如图中红色圈住部分所示,当 logback 内置的日志字段不能满足业务需求时,便可以借助 MDC 机制,将业务上想要输出的信息,通过 logback 给打印出来;
第二: 如蓝色圈住部分所示,当调用 MDC.remove(Key) 后,便可将业务字段从 MDC 中删除,日志中就不再打印请求 ID 啦;
趁热打铁,我们迅速看看在多线程情况下,使用 MDC 会发生什么现象呢?
还是基于上面的代码,把代码段放到了线程体内,稍微进行改造了一下,代码如下。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;
* MDC快速入门示例
* @author 程序要老王头
public class SimpleMDC {
public static void main(String[] args) {
new BizHandle("F0000").start();
new BizHandle("F9999").start();
class BizHandle extends Thread {
private static final Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
public static final String REQ_ID = "REQ_ID";
private String funCode;
public BizHandle(String funCode) {
this.funCode = funCode;
@Override
public void run() {
MDC.put(REQ_ID, UUID.randomUUID().toString());
logger.info("开始调用服务{},进行业务处理", funCode);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
logger.info(e.getMessage());