org.slf4j.LoggerFactory#getLogger 源码解析(含 log4j 和 logback )
1. LoggerFactory
查找slf4j的实现,并返回相应的LoggerFactory,如果有多个实现存在,则抛出异常。
log4j对应org.apache.logging.slf4j.Log4jLoggerFactory
logback对应ch.qos.logback.classic.LoggerContext
org.slf4j.LoggerFactory#getILoggerFactory
/**
* Return the {@link ILoggerFactory} instance in use.
* <p/>
* <p/>
* ILoggerFactory instance is bound with this class at compile time.
*
* @return the ILoggerFactory instance in use
*/
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:
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");
}
2. LoggerContext
2.1 log4j
查看是否通过LoggerFactory调用,如果不是则取当前LoggerContext,否则取调用类的类加载器所对应的LoggerContext。也就是说同一个应用可能有多个不同的LoggerContext。
org.apache.logging.slf4j.Log4jLoggerFactory#getContext
@Override
protected LoggerContext getContext() {
final Class<?> anchor = ReflectionUtil.getCallerClass(FQCN, PACKAGE);
return anchor == null ? LogManager.getContext() : getContext(ReflectionUtil.getCallerClass(anchor));
}
org.apache.logging.log4j.spi.AbstractLoggerAdapter#getContext(java.lang.Class<?>)
/**
* Gets the {@link LoggerContext} associated with the given caller class.
*
* @param callerClass the caller class
* @return the LoggerContext for the calling class
*/
protected LoggerContext getContext(final Class<?> callerClass) {
ClassLoader cl = null;
if (callerClass != null) {
cl = callerClass.getClassLoader();
}
if (cl == null) {
cl = LoaderUtil.getThreadContextClassLoader();
}
return LogManager.getContext(cl, false);
}
创建LoggerContext有很多策略,默认使用ClassLoaderContextSelector创建LoggerContext。
org.apache.logging.log4j.core.impl.Log4jContextFactory#createContextSelector
private static ContextSelector createContextSelector() {
try {
final ContextSelector selector = LoaderUtil.newCheckedInstanceOfProperty(Constants.LOG4J_CONTEXT_SELECTOR,
ContextSelector.class);
if (selector != null) {
return selector;
}
} catch (final Exception e) {
LOGGER.error("Unable to create custom ContextSelector. Falling back to default.", e);
}
return new ClassLoaderContextSelector();
}
org.apache.logging.log4j.core.selector.ClassLoaderContextSelector#getContext(java.lang.String, java.lang.ClassLoader, boolean, java.net.URI)
@Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
final URI configLocation) {
if (currentContext) {
final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
if (ctx != null) {
return ctx;
}
return getDefault();
} else if (loader != null) {
return locateContext(loader, configLocation);
} else {
final Class<?> clazz = ReflectionUtil.getCallerClass(fqcn);
if (clazz != null) {
return locateContext(clazz.getClassLoader(), configLocation);
}
final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
if (lc != null) {
return lc;
}
return getDefault();
}
}
2.2 logback
全局只有一个LoggerContext,即ch.qos.logback.classic.LoggerContext。
3. Logger
3.1 log4j
Logger的获取就相对简单了。对log4j来说是先获取LoggerContext对应的Map,如果有则直接返回,如果没有则创建Logger添加到Map。
org.apache.logging.log4j.spi.AbstractLoggerAdapter#getLogger
@Override
public L getLogger(final String name) {
final LoggerContext context = getContext();
final ConcurrentMap<String, L> loggers = getLoggersInContext(context);
final L logger = loggers.get(name);
if (logger != null) {
return logger;
}
loggers.putIfAbsent(name, newLogger(name, context));
return loggers.get(name);
}
3.2 logback
@Override
public final Logger getLogger(final String name) {
if (name == null) {
throw new IllegalArgumentException("name argument cannot be null");
}
// 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) {
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;
}
}
}
4. jcl-over-slf4j
实现slf4j和jcl的适配,将org.slf4j.Logger适配成org.apache.commons.logging.Log。
org.apache.commons.logging.impl.SLF4JLogFactory#getInstance(java.lang.String)
/**
* <p>
* Construct (if necessary) and return a <code>Log</code> instance, using
* the factory's current set of configuration attributes.
* </p>
*
* @param name
* Logical name of the <code>Log</code> instance to be returned
* (the meaning of this name is only known to the underlying logging
* implementation that is being wrapped)
*
* @exception LogConfigurationException
* if a suitable <code>Log</code> instance cannot be returned
*/
public Log getInstance(String name) throws LogConfigurationException {
Log instance = loggerMap.get(name);
if (instance != null) {
return instance;
} else {
Log newInstance;
Logger slf4jLogger = LoggerFactory.getLogger(name);
if (slf4jLogger instanceof LocationAwareLogger) {
newInstance = new SLF4JLocationAwareLog((LocationAwareLogger) slf4jLogger);
} else {
newInstance = new SLF4JLog(slf4jLogger);
}
Log oldInstance = loggerMap.putIfAbsent(name, newInstance);
return oldInstance == null ? newInstance : oldInstance;
}
}
总结
通过对log4j和backlog的源码解读,能明显感觉到backlog代码简明清晰。比较明显的logback的LoggerContext保持全局唯一而不再像log4j那样与classloader进行绑定,仅此一点就使代码的可读性大幅提升。