设为首页 加入收藏

TOP

Log4j2源码分析系列:(一)配置加载(一)
2019-09-17 16:28:17 】 浏览:37
Tags:Log4j2 源码 分析 系列 配置 加载

前言

在实际开发项目中,日志永远是一个绕不开的话题。本系列文章试图以slf4j和log4j2日志体系为例,从源码角度分析日志工作原理。

学习日志框架,首先要熟悉各类日志框架,这里推荐几篇文章,就不再赘述了。

https://www.cnblogs.com/rjzheng/p/10042911.html

https://www.cnblogs.com/chanshuyi/p/something_about_java_log_framework.html

https://blog.csdn.net/xktxoo/article/details/76359299

https://stackify.com/compare-java-logging-frameworks/

对于log4j2,配置文件有几类:properties、xml、json/jsn以及yaml/yml,平常我们用xml居多。

一般情况下,我们会创建log4j2.xml放到项目的/resources文件夹下。大部分使用maven管理依赖的项目也可能分环境配置,不同环境读取不同的log4j2文件,这时它一般在/profiles/${env}/文件夹下。

大多数人,应该是“借鉴”其他项目,把配置复制过来,再修修补补。然而你是否思考过:

  1. 为什么要写这个配置文件?不写的话会出什么问题?
  2. 这个配置文件的命名有什么规定吗?为什么我们平时见到的都是log4j2.xml,而不是其他名字?
  3. 这个配置文件是如何被加载的?

回答以上问题,就是本文的初衷。

提示

1. 本文会用调试的方法,以log4j2配置加载过程为主线,描述其工作流程;影响不大的旁枝细节会忽略,有兴趣的读者可自行查阅源码。

2. 多图预警!用电脑查看效果更佳。

3. 尽量动手操作,以加深理解。

 

环境准备

阅读源码前,请确保引入了slf4j和log4j2依赖包,以及适配包。以maven为例,本文示例程序引入了:

 <!-- slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <!-- bridge --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.7</version> </dependency> <!-- log4j2 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.7</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.7</version> </dependency>

源码

首先,我们新建一个java文件,打断点开始调试。

进入getLogger方法。可以看到,在LoggerFactory获取具体的Logger工厂。

进入getILoggerFactory方法。

这里的一堆逻辑先不要管,我们最终会进入418行。

接下来进入真正的日志绑定环节。由于我们只引入了log4j2,这里会直接找到它,继而绑定。StaticLoggerBinder就在log4j2的包中。

程序走到61行,可以看到这里使用饿汉方式实现了单例。41行实例化StaticLoggerBinder,会跳到53行,我们进去看看细节。

可以看出Log4jLoggerFactory继承了AbstractLoggerAdapter这个抽象的日志适配器。这个抽象适配器中定义了若干方法,别急,马上会提及。

回到LoggerFactory,通过方法getLoggerFactory,我们会得到刚刚创建出来的Log4jLoggerFactory:

接下来,我们进入log4j2的getLogger环节。

可以看到getLogger是个接口方法,并且有3个实现。

还记得我们刚才获取到的Log4jLoggerFactory吗?AbstractLoggerAdapter是它的父类,由此我们会走到AbstractLoggerAdapter的getLogger中。

getContext是AbstractLoggerAdapter的抽象方法,因此,我们下一步会走到Log4jLoggerFactory的getContext方法中。

这里用反射定位到我们的日志(anchor中文译为"锚",可以理解为类似文件句柄一类的东西),这里得出的anchor为"",因此会进入后面的语句。

最终,我们会拿到一个AppClassLoader,LogManager会利用这个类加载器获取上下文。

进入getContext看看:

请注意:这里的factory是Log4jContextFactory,它是在LogManager中的静态代码块中初始化的,具体细节后面会补充。

现在,我们先进入getContext看看:

这里的getContext是ContextSelector接口中的方法,下一步会进入ClassLoaderContextSelector中的getContext中。至于slector是怎么初始化的,我们放在后面一起说。

下面几步都是上下文相关操作,不再贴出,最终会回到这里:

然后走到152行的ctx.start方法,进去看下:

到现在,终于要开始加载配置了!!!

接下来几步比较直观,贴图示意:

在这里,先创建ConfigurationFactory的实例,然后获取配置。至于ConfigurationFactory的实例创建,这里不再说明,可自行查看。

接下来,进入getConfiguration方法:

进入该方法:

请注意,这里的getFactories已经很明显地告诉我们,这里有4个工厂(均继承自ConfigurationFactory ),分别处理前文提到的四类配置文件类型:properties、xml、json/jsn以及yaml/yml。调用factory.getSupportedTypes()方法即可获取到各类后缀。以xml为例:

其他类型文件同理。

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇报错:ORA-01461:仅能绑定要插入 .. 下一篇springboot启动报:Error creatin..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目