转载

log4j2源码解析(2)--LoggerContext

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对象的过程。

谢谢你请我吃糖果

原文  https://bryantchang.github.io/2018/09/15/log4j2-code-2/
正文到此结束
Loading...