

log4j2源码解析(2)--LoggerContext
source link: https://bryantchang.github.io/2018/09/15/log4j2-code-2/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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对象的过程。
谢谢你请我吃糖果
Recommend
-
70
In this article, I will explain how to separate the Mule application info and error logs into a separate log file. For instance, we have a Mule application that runs every 10 seconds to read an input file located on the...
-
53
title: 自定义log4j2发送日志到Kafka tags: log4j2,kafka 为了给公司的大数据平台提供各项目组的日志,而又使各项目组在...
-
43
-
30
日志文件自动删除功能必不可少,当然你可以让运维去做这事,只是这不地道。而日志组件是一个必备组件,让其多做一件删除的工作,无可厚非。本文就来探讨下 log4j 的日志文件自动删除实现吧。 0. 自动删除配置参考样例: (log4j2.x...
-
39
前言 一如既往的上班,一如既往的打开IDEA,一如既往的写下了这样的一行代码: private static final Logger log = LoggerFactory.getLogger(CommonController.class); 等等,写了这么久了,只知道这样可以很方便的使用log对象...
-
8
讲武德,你们要的高性能日志工具 Log4j2,来了 2020/11/23
-
15
SLF4JLoggerContext cannot be cast to LoggerContext ...
-
5
Logback 算是 JAVA 里一个老牌的日志框架,从 06 年开始第一个版本,迭代至今也十几年了。不过 logback 最近一个稳定版本还停留在 2017 年,好几年都没有更新;logback 的兄弟 slf4j 最近一个稳定版也是...
-
6
一次鞭辟入里的 Log4j2 日志输出阻塞问题的定位 线上某个应用的某个实例突然出现某些次请求服务响应极慢的情况,有几次请求超过 60s 才返回,并且通过日志发现,服务线程并没有做什么很重的操作。这种情况断断续...
-
9
张哈希apache1天前 线上某个应用的某个实例突然出现某些次请求服务响应极慢的情况,有几次请求超过 60s 才返...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK