Log4j2源码分析系列:(一)配置加载

2023-11-13

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

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

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

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

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

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

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

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

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

(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)

提示

  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为例:
在这里插入图片描述
其他类型文件同理。

好了,回到加载配置的方法,可以看到426行代码判断是否支持所有文件类型。其实最终的核心代码是453~467行:
在这里插入图片描述
这里尝试用不同的条件获取config,如果最终config为null,就会打印error日志,告诉你没有找到配置文件。由于目前我们还没有配置,就会走到466行。

现在,你可以在/resources路径下增加一个log4j2文件,填写一下简单配置,就会在459行得到config了。我们来看看getConfiguration的细节:

在这里插入图片描述
可以看出,这里就是按照各种条件拼接处配置文件的名字。

以最常见的log4j2.xml为例:
在这里插入图片描述
上图中,我们已经得到了配置文件的名字:log4j2.xml。

同时可以看到,prefix为log4j2,suffix为文件后缀。

其中prefix(505行)是写死在ConfigurationFactory中的:
在这里插入图片描述
所以,我们配置时定义的文件名,需要遵循规范,而不能随意命名。

现在有了配置文件名,就可以加载了:
在这里插入图片描述
进入方法内部:
在这里插入图片描述
现在,url已经获取到了。它的值是"项目路径/target/classes/log4j2.xml"。

后面的事情就是从文件加载内容( 517行,涉及到类加载器的知识,请自行查看)。

再然后,就是读取xml文件的内容啦:
在这里插入图片描述在这里插入图片描述
走到这里,就开始读取xml文件了。这部分内容且待下回分解。

遗留问题:LoggerManager的factory及其内部的selector是怎么初始化的?

其实,在调用LogManager.getContext(cl, false);之前,LoggerManager中的静态代码块会提前被调用,我们看一下:
在这里插入图片描述
我们看89~100行代码即可:
在这里插入图片描述
进入方法ProviderUtil.getProviders()内部查看:
在这里插入图片描述
在这里插入图片描述
可以看到provider是使用懒汉方式实现的单例(你会发现89行代码中ProviderUtil.hasProviders()方法执行时已经创建过了,因此这里直接返回。注意创建过程有个细节,后面要用到),用于确定各个factory的优先级。

我们重点看91行代码内部细节:
在这里插入图片描述
在这里插入图片描述
在96行,加载class,98行又将其转换为LoggerContextFactory的子类(也就是Log4jContextFactory)。

那么问题来了,className是啥,为啥它指定了Log4jContextFactory?

其实,在前面创建Provider实例时,构造器中会读取log4j-core中的配置文件,其中就包含className对应的属性:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
就这样,得到了className:org.apache.logging.log4j.core.impl.Log4jContextFactory。

接着往下走:
在这里插入图片描述
\可以看到这里会用反射的方式实例化Log4jContextFactory对象,会调用Log4jContextFactory的无参构造器:
在这里插入图片描述
createContextSelector方法,就会初始化selector啦:
在这里插入图片描述
后续的初始化细节就不再展开啦。

最后会走到这里:
在这里插入图片描述
至此,factory创建完毕。

现在,你应该可以回答文首的三个问题了吧?

总结

本文通过调试,描述了log4j2日志配置加载的主线(忽略了很多细节,比如可以配置path等等),后续的文章将会进一步描述配置文件的解析过程。

希望读者通过本文,能够对log4j2的配置加载过程有更为深入的理解。

最后,作者水平有限,难免错漏,欢迎指正及交流,共同进步。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Log4j2源码分析系列:(一)配置加载 的相关文章

  • ElasticBeanstalk Java,Spring 活动配置文件

    我正在尝试通过 AWS ElasticBeanstalk 启动 spring boot jar 一切正常 配置文件为 默认 有谁知道如何为 java ElasticBeanstalk 应用程序 不是 tomcat 设置活动配置文件 spri
  • Java程序中的数组奇怪的行为[重复]

    这个问题在这里已经有答案了 我遇到了这个 Java 程序及其以意想不到的方式运行 以下程序计算 int 数组中元素对之间的差异 import java util public class SetTest public static void
  • Android Studio 在编译时未检测到支持库

    由于 Android Studio 将成为 Android 开发的默认 IDE 因此我决定将现有项目迁移到 Android studio 中 项目结构似乎不同 我的项目中的文件夹层次结构如下 Complete Project gt idea
  • JNI 不满意链接错误

    我想创建一个简单的 JNI 层 我使用Visual studio 2008创建了一个dll Win 32控制台应用程序项目类型 带有DLL作为选项 当我调用本机方法时 出现此异常 Exception occurred during even
  • Java8无符号算术

    据广泛报道 Java 8 具有对无符号整数的库支持 然而 似乎没有文章解释如何使用它以及有多少可能 有些函数 例如 Integer CompareUnsigned 很容易找到 并且似乎可以实现人们所期望的功能 但是 我什至无法编写一个简单的
  • CXF Swagger2功能添加安全定义

    我想使用 org apache cxf jaxrs swagger Swagger2Feature 将安全定义添加到我的其余服务中 但是我看不到任何相关方法或任何有关如何执行此操作的资源 下面是我想使用 swagger2feature 生成
  • 使用 ANTLR 为 java 源代码生成抽象语法树

    如何使用 ANTLR 从 java src 代码生成 AST 有什么帮助吗 好的 步骤如下 前往ANTLR站点 http www antlr org 并下载最新版本 下载Java g和JavaTreeParser g文件来自here htt
  • 如何在 Java 中禁用 System.out 以提高速度

    我正在用 Java 编写一个模拟重力的程序 其中有一堆日志语句 到 System out 我的程序运行速度非常慢 我认为日志记录可能是部分原因 有什么方法可以禁用 System out 以便我的程序在打印时不会变慢 或者我是否必须手动检查并
  • hibernate总是自己删除表中的所有数据

    您好 我正在开发一个 spring mvc 应用程序 它使用 hibernate 连接到存储文件的 mysql 数据库 我有两个方法 一个方法添加我选择的特定文件路径中的所有文件 另一种方法调用查询以返回从 mysql 存储的文件列表 问题
  • 如何在jsp代码中导入java库?

    我有以下jsp代码 我想添加 java io 等库 我怎样才能做到这一点
  • OnClick 事件中的 finish() 如何工作?

    我有一个Activity一键退出Activity 通过layout xml我必须设置OnClick事件至cmd exit调用 this finish 效果很好 public void cmd exit View editLayout thi
  • 无法理解 Java 地图条目集

    我正在看一个 java 刽子手游戏 https github com leleah EvilHangman blob master EvilHangman java https github com leleah EvilHangman b
  • 迁移到 java 17 后有关“每个进程的内存映射”和 JVM 崩溃的 GC 警告

    我们正在将 java 8 应用程序迁移到 java 17 并将 GC 从G1GC to ZGC 我们的应用程序作为容器运行 这两个基础映像之间的唯一区别是 java 的版本 例如对于 java 17 版本 FROM ubuntu 20 04
  • 如何将文件透明地传输到浏览器?

    受控环境 IE8 IIS 7 ColdFusion 当从 IE 发出指向媒体文件 例如 mp3 mpeg 等 的 GET 请求时 浏览器将启动关联的应用程序 Window Media Player 我猜测 IIS 提供文件的方式允许应用程序
  • 反思 Groovy 脚本中声明的函数

    有没有一种方法可以获取 Groovy 脚本中声明的函数的反射数据 该脚本已通过GroovyShell目的 具体来说 我想枚举脚本中的函数并访问附加到它们的注释 Put this到 Groovy 脚本的最后一行 它将作为脚本的返回值 a la
  • org.jdesktop.application 包不存在

    几天以来我一直在构建一个 Java 桌面应用程序 一切都很顺利 但是今天 当我打开Netbeans并编译文件时 出现以下编译错误 Compiling 9 source files to C Documents and Settings Ad
  • 当单元格内的 JComboBox 中有 ItemEvent 时,如何获取 CellRow

    我有一个 JTable 其中有一列包含 JComboBox 我有一个附加到 JComboBox 的 ItemListener 它会根据任何更改进行操作 但是 ItemListener 没有获取更改的 ComboBox 所在行的方法 当组合框
  • android Accessibility-service 突然停止触发事件

    我有一个 AccessibilityService 工作正常 但由于开发过程中的某些原因它停止工作 我似乎找不到这个原因 请看一下我的代码并告诉我为什么它不起作用 public class MyServicee extends Access
  • JAVA - 如何从扫描仪读取文件中检测到“\n”字符

    第一次海报 我在读取文本文件的扫描仪中读取返回字符时遇到问题 正在读取的文本文件如下所示 test txt start 2 0 30 30 1 1 90 30 0 test txt end 第一行 2 表示两个点 第二行 位置索引 0 xp
  • java8 Collectors.toMap() 限制?

    我正在尝试使用java8Collectors toMap on a Stream of ZipEntry 这可能不是最好的想法 因为在处理过程中可能会发生异常 但我想这应该是可能的 我现在收到一个我不明白的编译错误 我猜是类型推理引擎 这是

随机推荐

  • 微软晓晓朗读录音工具windows-文字转语音

    微软Edge的 大声朗读 功能大家都用过吧 这个功能非常好用 但是美中不足的是它不能保存语音文件 所以就有大神提取了edge的功能和接口作了这个软件 使用很简单 直接输入文字 然后点击 朗读 软件就会识别文字 然后自动播放语音 在下方的设置
  • sqli-labs第九十关

    这两关是时间盲注 Less 9 GET Bilnd Time based String Quotes 手工注入 这里使用sleep 函数 闭合方式还是跳过 id 1 1 1 爆破数据库 id 1 and if ascii substr da
  • 【学习笔记】rabbitmq设置队列ttl和使用延迟插件的代码示例

    文章目录 设置队列ttl 配置文件 生产者 消费者 设置消息ttl 延迟插件的使用 修改配置文件 修改生产者 修改消费者 设置队列ttl 代码架构 创建两个队列QA和QB 两者队列TTL分别设置为10S和40S 然后在创建一个交换机X和死信
  • 阿里P8大神讲解——Java,JVM内存模型

    在Java程序界流行着一种默认的说法叫 黄金5年 也就是一个程序员从入职的时间开始算起 前五年的选择直接影响着整个职业生涯发展方向和薪资走向 如何走好这5年很关键 如何彻底从一个菜鸟蜕变成 可以以不变应万变的职业大牛 这是一个涉及到自身专业
  • 朋友去华为面试,轻松拿到26K的Offer,羡慕了......

    最近有朋友去华为面试 面试前后进行了20天左右 包含4轮电话面试 1轮笔试 1轮主管视频面试 1轮hr视频面试 据他所说 80 的人都会栽在第一轮面试 要不是他面试前做足准备 估计都坚持不完后面几轮面试 其实 第一轮的电话面试除了一些常规的
  • 【电气专业知识问答】问:在何种事故情况下应立即停用电动机?

    电气专业知识问答 问 在何种事故情况下应立即停用电动机 答 1 发生危急人身安全情况 需要立即停用电动机的 2 电动机所带机械设备损坏至危险程度时 3 电动机起火冒烟 4 电动机强烈振动 窜轴或内部发生定 转子碰擦 5 电动机缺相运行 6
  • C#读写各类文件合集

    C 文件操作合集 一 利用字节流与文件流读写txt json文件 1 以文件的方式进行操作 2 以文件流的方式进行读写 3 以二进制数据流的方式进行读写 4 以文本流的方式进行读写 5 JOSN文件的读写 二 kernel32读写ini文件
  • 【论文笔记】疯狂的检测工具 —— 静态分析工具

    本文目标 精度论文 CryptoGuard High Precision Detection of Cryptographic Vulnerabilities in Massive sized Java Projects 主要针对大规模的
  • 【Pytorch Lighting】第 9 章:部署和评分模型

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Win10系统安装使用H3C HCL实验室(最新版)方法

    如果安装了wsl2虚拟机的 首先参照此教程将wsl版本降为1 https blog csdn net qq 26123545 article details 120169070 spm 1001 2014 3001 5501 如果之前的HC
  • CMake编译.dll并使用开源库SDE

    目录 环境 项目修改 编译 dll文件并确保它能正常使用 测试使用 dll文件 文件下载链接 环境 本篇博客所要做的是 Win10环境使用CMake编译一个开源C 库 这个库所有 h头文件和 cpp源文件都有了 将这个库编译为动态链接库 d
  • armbian安装图形桌面_Linux桌面环境(桌面系统)大比拼「附带优缺点」

    早期的 Linux 系统都是不带界面的 只能通过命令来管理 比如运行程序 编辑文档 删除文件等 所以 要想熟练使用 Linux 就必须记忆很多命令 后来随着 Windows 的普及 计算机界面变得越来越漂亮 点点鼠标就能完成很多工作 人们已
  • x86-64 汇编基础 ---- 记读 《CS: APP》

    x86 64 汇编基础 记读 CS APP 通常情况下 使用现代的优化编译器产生的代码至少与一个熟练的汇编语言程序员手工编写的代码一样有效 1 看懂汇编码 1 汇编码的格式 ATT格式 这是GCC OBJDUMP和其它一些工具的常用格式 由
  • 推荐系统实战2——EasyRec 推荐框架环境配置

    推荐系统实战2 EasyRec 推荐框架环境配置 学习前言 先验条件 EasyRec仓库地址 EasyRec环境配置 一 EasyRec的下载 二 EasyRec的初始化 三 EasyRec的安装 四 一些额外的情况 学习前言 EasyRe
  • opencv 读取NV12格式(.yuv)文件,并转为RGB格式保存为JPG

    实测代码如下 include
  • OpenStack的搭建与使用

    初次接触open stack与Linux 如有错误与可改进的地方 恳请指出 一 搭建 一 配置推荐 系统 镜像 内存 储存 Linux centos7 6 16G 100G 二 前期准备 1 开启虚拟化 图2 1 开启虚拟化 2 关闭防火墙
  • C语言典型例题四——斐波那契数列

    Fibonacci 斐波那契 数列 求斐波那契数列的前40个数 这个数列有个特点 第1 2两个数为1 1 从第三个数开始 该数是其前面两个数之合 即该数列为1 1 2 3 5 8 13 这是一个有趣的古典数学问题 有一对兔子 从出生后第三个
  • python 在Excel中新增一列

    1 在Excel中定义新列 定义新列需要用到columns tolist 函数 具体代码如下 col name df columns tolist col name insert 新列位置 新列名称 wb df reindex column
  • Log4j2源码分析系列:(一)配置加载

    在实际开发项目中 日志永远是一个绕不开的话题 本系列文章试图以slf4j和log4j2日志体系为例 从源码角度分析日志工作原理 学习日志框架 首先要熟悉各类日志框架 这里推荐两篇文章 就不再赘述了 https www cnblogs com
  • C——选择结构

    选择结构 1 关系运算与逻辑运算 1 1 关系运算 1 2 逻辑运算 2 if语句 2 1 单分支的if语句 2 2 双分支的if语句 3 条件运算符 4 switch语句 1 关系运算与逻辑运算 C语言中的逻辑值 C语言将 非0 值当做值