美文网首页
slf4j中的MDC工具类使用

slf4j中的MDC工具类使用

作者: tom_xin | 来源:发表于2019-03-24 21:21 被阅读0次

    MDC是SLF4J中的一个类,通过MDC我们可以很方便的实现同一个线程内(包括父线程和子线程之间)的日志的追踪。对于Web项目来讲,在MDC的帮助下,很方便的帮我们找出一次请求中所有的日志。下面我们先来看一下一个简单的demo。


mdc-demo

    1、首先我们需要一个Java的Maven项目这里我们取名为mdc-demo。
    2、引入我们需要的依赖包slf4j和logback

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.21</version>
    </dependency>

    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>

    3、新建logback.xml文件,配置日志打印格式,如果想打印MDC中的内容,需要在appender的pattern中配置。

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd hh:mm:ss} [%thread] [traceId = %X{traceId}] [%logger{32}] - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
    </root>
</configuration>

补充以下,为什么配置文件要取名为logback.xml,因为logback在读取配置文件时会默认读取resources目录下的logback命名的xml文件。源码如下,在ContextInitializer的类中。

    final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy";
    final public static String AUTOCONFIG_FILE = "logback.xml";
    final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
    final public static String CONFIG_FILE_PROPERTY = "logback.configurationFile";

    public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
        URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

        url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

        url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

        return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
    }

    4、直接写Java代码就可以了,运行查看效果就可以了。

public class MdcTest {

    private static Logger logger = LoggerFactory.getLogger(MdcTest.class);

    public static void main(String[] args) {
        MDC.put("traceId", UUID.randomUUID().toString());
        logger.info("MdcTest");
        MDC.clear();
        logger.info("clear test");
        MDC.remove("traceId");
    }
}

输入结果如下,成功的打印出我们的traceId了。

2019-03-24 04:21:23 [main] [traceId = b48f3382-e71a-4fd1-a411-0e20661b2ccf] [com.tom.mdc.MdcTest] - MdcTest
2019-03-24 04:21:23 [main] [traceId = ] [com.tom.mdc.MdcTest] - clear test

这里我把MDC.put(key,value)中的key命名为了traceId,只要跟logback.xml中的自己命名的变量名对应起来就可以了。
具体mdc-demo我放在了github上,地址:


MDC工作原理

    看了demo之后,我们再来一起看一下,MDC是如何把traceId放入每行日志中的,这里以logback为例来讲解。
    1、先来看一下MDC类自身的结构,MDC类中提供了get,put,remove,clear四个常用的增删改查接口,getCopyOfContextMap方法获取整个线程上下文中存入的Map值,返回的是一个新的Map。可以防止源Map被改动后,影响已获取Map的正常使用。


    public static void put(String key, String val) throws IllegalArgumentException
    
    public static String get(String key) throws IllegalArgumentException
    
    public static void remove(String key) throws IllegalArgumentException

    public static void clear()

    public static Map<String, String> getCopyOfContextMap()

    public static void setContextMap(Map<String, String> contextMap)

    再来看一下MDC类的初始化,MDC类中持有一个MDCAdapter接口类,在初始化时静态代码块中,会调用bwCompatibleMDCAdapterFromBinder方法,在该方法中再调用StaticMDCBinder的getMDCA()方法,获取MDCAdapter的一个实现。

private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
        try {
            return StaticMDCBinder.getSingleton().getMDCA();
        } catch (NoSuchMethodError nsme) {
            // binding is probably a version of SLF4J older than 1.7.14
            return StaticMDCBinder.SINGLETON.getMDCA();
        }
    }

    static {
        try {
            mdcAdapter = bwCompatibleGetMDCAdapterFromBinder();
        } catch (NoClassDefFoundError ncde) {
            mdcAdapter = new NOPMDCAdapter();
            String msg = ncde.getMessage();
            if (msg != null && msg.contains("StaticMDCBinder")) {
                Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");
                Util.report("Defaulting to no-operation MDCAdapter implementation.");
                Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details.");
            } else {
                throw ncde;
            }
        } catch (Exception e) {
            // we should never get here
            Util.report("MDC binding unsuccessful.", e);
        }
    }

     2、MDC类的继承关系

image.png
    从上图中可以看出来,MDC类中持有一个MDCAdapter的接口,MDCAdapter接口有三个实现类,分别是BasicMDCAdapter,LogbackMDCAdapter,NOPMDCAdapter这三个类。其中BasicMDCAdapter,NOPMDCAdapter类是SLF4J框架中实现的。LogbackMDCAdapter是logback框架实现的。
    下面是MDC初始化时的一段代码,静态代码块中,调用了bwCompatibleGetMDCAdapterFromBinder()这个方法来初始化mdcAdapter接口,
再看bwCompatibleGetMDCAdapterFromBinder()方法的实现,调用了StaticMDCBinder类的getMDCA()方法。StaticMDCBinder类的实现很简单,getMDCA()的方法,直接返回了一个LogbackMDCAdapter的实例。
    private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
        try {
            return StaticMDCBinder.getSingleton().getMDCA();
        } catch (NoSuchMethodError nsme) {
            // binding is probably a version of SLF4J older than 1.7.14
            return StaticMDCBinder.SINGLETON.getMDCA();
        }
    }

    static {
        try {
            mdcAdapter = bwCompatibleGetMDCAdapterFromBinder();
        } catch (NoClassDefFoundError ncde) {
            mdcAdapter = new NOPMDCAdapter();
            String msg = ncde.getMessage();
            if (msg != null && msg.contains("StaticMDCBinder")) {
                Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");
                Util.report("Defaulting to no-operation MDCAdapter implementation.");
                Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details.");
            } else {
                throw ncde;
            }
        } catch (Exception e) {
            // we should never get here
            Util.report("MDC binding unsuccessful.", e);
        }
    }

public class StaticMDCBinder {

    /**
     * The unique instance of this class.
     */
    public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();

    private StaticMDCBinder() {
    }

    /**
     * Currently this method always returns an instance of 
     * {@link StaticMDCBinder}.
     */
    public MDCAdapter getMDCA() {
        return new LogbackMDCAdapter();
    }

    public String getMDCAdapterClassStr() {
        return LogbackMDCAdapter.class.getName();
    }
}

    3、traceId如何注入到打印的日志中

相关文章

网友评论

      本文标题:slf4j中的MDC工具类使用

      本文链接:https://www.haomeiwen.com/subject/erbhvqtx.html