Logback学习交流群:603634815。
Logback是一个日志框架。
其中有一项功能很好使-MDC,映射诊断环境(Mapped Diagnostic Context)。
MDC本质上是使用的ThreadLocal。系统调用链可能很长,为了方便日志跟踪,统一打印标识。
以下是一个官方入门例子:
/**
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2015, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package chapters.mdc;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.Loader;
import ch.qos.logback.core.util.StatusPrinter;
public class SimpleMDC {
static public void main(String[] args) throws Exception {
// You can put values in the MDC at any time. Before anything else
// we put the first name
MDC.put("first", "Dorothy");
// configure via the configuration file "chapters/mdc/simpleMDC.xml"
// which ships with the examples
//configureViaXML_File();
// For educational purposes, the same configuration can
// be accomplished programmatically.
//
programmaticConfiguration();
Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
// We now put the last name
MDC.put("last", "Parker");
// The most beautiful two words in the English language according
// to Dorothy Parker:
logger.info("Check enclosed.");
logger.debug("The most beautiful two words in English.");
MDC.put("first", "Richard");
MDC.put("last", "Nixon");
logger.info("I am not a crook.");
logger.info("Attributed to the former US president. 17 Nov 1973.");
}
static void programmaticConfiguration() {
// Configure logback
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.reset();
PatternLayoutEncoder layout = new PatternLayoutEncoder();
layout.setContext(loggerContext);
layout.setPattern("%X{first} %X{last} - %m%n");
layout.start();
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
appender.setContext(loggerContext);
appender.setEncoder(layout);
appender.start();
// cast root logger to c.q.logback.classic.Logger so that we can attach
// an appender to it
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("root");
root.addAppender(appender);
}
static void configureViaXML_File() {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
lc.reset();
URL url = Loader.getResourceBySelfClassLoader("chapters/mdc/simpleMDC.xml");
try {
url = url==null?new File("src/main/java/chapters/mdc/simpleMDC.xml").toURI().toURL():url;
} catch (MalformedURLException e) {
e.printStackTrace();
}
configurator.doConfigure(url);
} catch (JoranException je) {
StatusPrinter.print(lc);
}
}
}
MDC.put(String, String); 是将变量内容存到线程内部。看他的内部实现
public static void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key parameter cannot be null");
}
if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
}
mdcAdapter.put(key, val);
}
是通过mdcAdapter存放的,类型org.slf4j.spi.MDCAdapter,是slf4j提供的一个统一接口。
它是通过一个静态块进行初始化的:
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);
}
}
StaticMDCBinder是org.slf4j.impl.StaticMDCBinder,在logback-classic包里面。但是这段代码是在slf4j-api中的,所以,只要使用slf4j作为统一日志门面的日志框架在使用MDC功能时一定要在自己的jar包里面创建org.slf4j.impl.StaticMDCBinder,包名、类名不能错。
org.slf4j.impl.StaticMDCBinder里面 getMDCA()方法,直接返回的是new LogbackMDCAdapter();
ch.qos.logback.classic.util.LogbackMDCAdapter实现org.slf4j.spi.MDCAdapter,声明ThreadLocal:
final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();
进行get、set方法。
因为程序里面配置了pattern属性
layout.setPattern("%X{first} %X{last} - %m%n");
所以在日志打印的时候会从MDC中取出first和last进行打印。
如果是以下配置文件,也能起到同样作用:
<?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>%X{first} %X{last} - %m%n</Pattern>
</layout>
</appender>
<root level="debug">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
未完待续:MDC取值过程。