日志框架统一解决方案(mvn.exclude)
slf4j门面设计整合spring的日志方案
通过 lombok 引入的 slf4j 版本有点旧,不小心就遇到静态绑定的 “古老的”方案
slf4j 1.8+ 改成了 JDK6 所推崇的 SPI
静态绑定,简单说就是在第三方实现库(例如 logback)中放接口库(org.slf4j…XxxBinder)的代码来引入自己的实现,放个图就懂了
package org.slf4j;
public final class LoggerFactory {
// 注意:slf4j.LoggerFactory 并没有实现 ILoggerFactory 接口
// slf4j.LoggerFactory 是用来 获取 第三方厂商的ILoggerFactory 的
public static Logger getLogger(Class<?> clazz) {
// step into ...
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
autoComputedCallingClass.getName()));
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
public static Logger getLogger(String name) {
// step into ...
// 先获取 LoggerFactory
ILoggerFactory iLoggerFactory = getILoggerFactory();
// step into ...
// 再获取 Logger
return iLoggerFactory.getLogger(name);
}
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
// 初始化成功后
case SUCCESSFUL_INITIALIZATION:
// step into ...
// 第三方厂商会有一个 jar/org/slf4j/impl/StaticLoggerBinder 这样子的类路径
// slf4j 1.7 版本的做法:由第三方类通过 实现绑定类 来做到静态绑定
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
}
-------
// 此时是在 第三方的jar目录中(这里使用 logback-classic 演示)
package org.slf4j.impl;
public class StaticLoggerBinder implements LoggerFactoryBinder {
// step into ...
// 3. logback 初始化 loggerFactory、logger
// public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle
// 可见: LoggerContext is a ILoggerFactory
// 看看 默认的LoggerFactory 的构造
private LoggerContext defaultLoggerContext = new LoggerContext();
private StaticLoggerBinder() {
defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
}
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}
// 这意味着 LoggerContext(LoggerFactory) 是1个单例
private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
static {
// step into ...
SINGLETON.init();
}
void init() {
try {
try {
// 4. 读取日志配置文件
// step into autoConfig() ...
new ContextInitializer(defaultLoggerContext).autoConfig();
} catch (JoranException je) {
Util.report("Failed to auto configure default logger context", je);
}
// logback-292
if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
}
contextSelectorBinder.init(defaultLoggerContext, KEY);
initialized = true;
} catch (Exception t) { // see LOGBACK-1159
Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
}
}
public ILoggerFactory getLoggerFactory() {
if (!initialized) {
// 如果没有完成初始化就需要获取1个LoggerFactory,则返回默认的LoggerFactory
return defaultLoggerContext;
}
if (contextSelectorBinder.getContextSelector() == null) {
throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
}
return contextSelectorBinder.getContextSelector().getLoggerContext();
}
}
package ch.qos.logback.classic;
public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
// 单例的根节点
final Logger root;
private Map<String, Logger> loggerCache;
// LoggerFactory 构造
public LoggerContext() {
super();
this.loggerCache = new ConcurrentHashMap<String, Logger>();
this.loggerContextRemoteView = new LoggerContextVO(this);
// 初始化Logger根节点
this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
this.root.setLevel(Level.DEBUG);
// 缓存Logger根节点
loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
initEvaluatorMap();
size = 1;
this.frameworkPackages = new ArrayList<String>();
}
public final Logger getLogger(final Class<?> clazz) {
return getLogger(clazz.getName());
}
@Override
public final Logger getLogger(final String name) {
if (name == null) {
throw new IllegalArgumentException("name argument cannot be null");
}
// 直接返回 Logger 根节点
// if we are asking for the root logger, then let us return it without
// wasting time
if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
return root;
}
int i = 0;
Logger logger = root;
// check if the desired logger exists, if it does, return it
// without further ado.
Logger childLogger = (Logger) loggerCache.get(name);
// if we have the child, then let us return it without wasting time
if (childLogger != null) {
return childLogger;
}
// if the desired logger does not exist, them create all the loggers
// in between as well (if they don't already exist)
String childName;
while (true) {
// 循环获取(并缓存)下一层级的Logger,直至当前类的Logger
// 层级按照包的分隔符(.)来分隔
// 当找到当前类层级的logger即返回
int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
if (h == -1) {
childName = name;
} else {
childName = name.substring(0, h);
}
// move i left of the last point
i = h + 1;
synchronized (logger) {
childLogger = logger.getChildByName(childName);
if (childLogger == null) {
childLogger = logger.createChildByName(childName);
loggerCache.put(childName, childLogger);
incSize();
}
}
logger = childLogger;
if (h == -1) {
return childLogger;
}
}
}
final void noAppenderDefinedWarning(final Logger logger) {
if (noAppenderWarning++ == 0) {
// TODO 有说法!!!
getStatusManager().add(new WarnStatus("No appenders present in context [" + getName() + "] for logger [" + logger.getName() + "].", logger));
}
}
}
// ch.qos.logback.classic.util.ContextInitializer#autoConfig
public void autoConfig() throws JoranException {
StatusListenerConfigHelper.installIfAsked(loggerContext);
// 从默认的文件中获取有效的路径
// step into ...
URL url = findURLOfDefaultConfigurationFile(true);
if (url != null) {
configureByResource(url);
} else {
Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
if (c != null) {
try {
c.setContext(loggerContext);
c.configure(loggerContext);
} catch (Exception e) {
throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass()
.getCanonicalName() : "null"), e);
}
} else {
BasicConfigurator basicConfigurator = new BasicConfigurator();
basicConfigurator.setContext(loggerContext);
basicConfigurator.configure(loggerContext);
}
}
}
// ch.qos.logback.classic.util.ContextInitializer#findURLOfDefaultConfigurationFile
public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
if (url != null) {
return url;
}
// final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
if (url != null) {
return url;
}
// final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy";
url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);
if (url != null) {
return url;
}
// final public static String AUTOCONFIG_FILE = "logback.xml";
return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
}