LoggerContext作用及初始化流程
根据我们在Log4j初识中的实例可以看出,在不适用日志门面插件slf4j的情况下,获取logger的方式一般为
Logger logger = logManager.getLogger(xx.class)
可以看到,通常情况下,我们会使用LogManager的静态方法getLogger方法获取logger,下面我们就简要看一下这个方法的流程。
public static Logger getLogger(final Class<?> clazz) {
final Class<?> cls = callerClass(clazz);
return getContext(cls.getClassLoader(), false).getLogger(toLoggerName(cls));
}
这个方法返回了一个链式调用后的结果,而首先是先调用了getContext,这个方法返回的是一个LoggerContext对象,该方法定义如下:
public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) {
try {
return factory.getContext(FQCN, loader, null, currentContext);
} catch (final IllegalStateException ex) {
LOGGER.warn(ex.getMessage() + " Using SimpleLogger");
return new SimpleLoggerContextFactory().getContext(FQCN, loader, null,currentContext);
}
}
这个方法中的LoggerContext是通过LoggerFactory的getContext方法,默认情况下,对应的子类是Log4jLoggerFactory。对应的方法如下:
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext, final boolean currentContext) {
//获得LoggerContex实例
final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext);
if (externalContext != null && ctx.getExternalContext() == null) {
ctx.setExternalContext(externalContext);
}
//启动LoggerContext
if (ctx.getState() == LifeCycle.State.INITIALIZED) {
ctx.start();
}
return ctx;
}
其中里面的主要逻辑是返回一个启动的LoggerContext实例,其中LoggerContext实例是通过Log4jLoggerFactory中的ContextSelector获取的,默认情况下使用的是ClassLoaderContextSelector,在Log4jLoggerFactory的构造方法中进行初始化。
完成初始化后,就是LoggerContext的启动,如果当前的context实例的状态为初始化状态,则调用start函数,其中的流程是
LogContext.start()—>LogContext.reconfigure()—>LogContext.reconfigure(URI)
其中主要做的事情就是根据配置文件的位置,读取相应的log4j2配置文件,解析配置文件,最终解析为各种Appender以及Logger的Java对象,下面我们来看大致的流程
首先会获取ConfigurationFactory的实例,然后获取Configuration,分别看这两步
1、获取ConfigurationFactory实例
public static ConfigurationFactory getInstance() {
// volatile works in Java 1.6+, so double-checked locking also works properly
//noinspection DoubleCheckedLocking
if (factories == null) {
LOCK.lock();
try {
if (factories == null) {
final List<ConfigurationFactory> list = new ArrayList<>();
final String factoryClass = PropertiesUtil.getProperties().getStringProperty(CONFIGURATION_FACTORY_PROPERTY);
if (factoryClass != null) {
addFactory(list, factoryClass);
}
final PluginManager manager = new PluginManager(CATEGORY);
manager.collectPlugins();
final Map<String, PluginType<?>> plugins = manager.getPlugins();
final List<Class<? extends ConfigurationFactory>> ordered = new ArrayList<>(plugins.size());
for (final PluginType<?> type : plugins.values()) {
try {
ordered.add(type.getPluginClass().asSubclass(ConfigurationFactory.class));
} catch (final Exception ex) {
LOGGER.warn("Unable to add class {}", type.getPluginClass(), ex);
}
}
Collections.sort(ordered, OrderComparator.getInstance());
for (final Class<? extends ConfigurationFactory> clazz : ordered) {
addFactory(list, clazz);
}
// see above comments about double-checked locking
//noinspection NonThreadSafeLazyInitialization
factories = Collections.unmodifiableList(list);
}
} finally {
LOCK.unlock();
}
}
LOGGER.debug("Using configurationFactory {}", configFactory);
return configFactory;
这是一个典型的单例模式,使用了双重检查锁的方式获取实例,这个方法里面有一个操作步骤我们要重点介绍,就是PluginManager实例的创建,PluginManager主要用于管理Log4J中的插件,这些Plugin主要是一些对应不同格式的配置文件的解析器,PluginManager中使用category对不同类型的插件进行缓存。
获取到ConfigurationFactory的实例之后,就是获取Configuration的过程,主要流程是根据配置的格式,从遍历上面提到列表中的factory,加载到对应的ConfigurationFactory,随后通过这个Factory去解析配置文件最终获取到Configuration。代码流程如下:
public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation, final ClassLoader loader) {
if (!isActive()) {
return null;
}
if (loader == null) {
return getConfiguration(loggerContext, name, configLocation);
}
if (isClassLoaderUri(configLocation)) {
final String path = extractClassLoaderUriPath(configLocation);
final ConfigurationSource source = ConfigurationSource.fromResource(path,loader);
if (source != null) {
final Configuration configuration = getConfiguration(loggerContext,source);
if (configuration != null) {
return configuration;
}
}
}
return getConfiguration(loggerContext, name, configLocation);
}
注意这里的getConfiguration(loggerContext,source);是一个多态方法,会根据不同类型的配置发文件,调用相应的ConfigureFactory的方法,将配置文件中的tag变为java对象。下一篇文章中,我会以xml类型的文件详细介绍将tag变化为Java对象的过程。
谢谢你请我吃糖果