Log4j源码解析
Log4j源码解析
主要流程
Logger logger = Logger.getLogger(Main.class);
1、通过Logger.getLogger(Class clazz) 或 Logger.getLogger(String name)进入。
2、加载LogManager进jvm, 执行静态代码块执行初始化, 创建出RepositorySelector实例及LoggerRepository实例(Hierarchy)。
3、调用OptionConverter.selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy)方法执行配置. (传递Hierarchy)。
4、在OptionConverter中根据存在的配置文件类型创建Configurator实例(这里假设配置文件为log4j.properties), 执行Configurator的doConfigure(URL url, LoggerRepository repository) (传递Hierarchy)。
5、在PropertyConfigurator完成所有组件配置。
6、configureRootCategory(Properties props, LoggerRepository hierarchy) 配置根logger及其appdender等属性。
7、parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) 配置非根的logger及其appdender等属性, 渲染器等。
8、LogManager完成静态代码块后, 通过Hierarchy.getLogger(String name)方法获取出logger对象。
logger.info(“xxx”);
1、从main方法出发, 这里示例是调用info()方法. (故后续都是判断info级别)。
2、判断是否满足全局的level级别>=info, 满足则 再判断本logger的level级别是否>=info(本logger没有值则找其父类直至有值)。
3、全局level或本体系的level有其一不满足>=info, 提前结束流程。
4、执行本logger再递归父类logger, 执行其aai.appendLoopOnAppenders()方法 (注: 当前logger执行完会判断additive属性, 为false则不再递归父类, 该点在流程图第20步体现)。
5、遍历当前logger的日志输出器appender, 执行其doAppend()方法
6、进入appender的公共父类AppenderSkeleton的doAppend()方法, 先判断当前appender的日志级别level是否>=info。
7、当前appender日志级别level<info, 结束该appender的执行, 遍历下一个appender。
8、递归执行本appender的过滤器执行链。
9、Filter中进行过滤判定。
10、不满足过滤条件则结束该appender的执行, 遍历下一个appender。
11、执行当前appender的append()方法. (这里假设实现类为RollingFileAppender)。
12、进入appender的公共父类WriterAppender的append()方法, 执行子类RollingFileAppender的subAppend()方法。
13、进入RollingFileAppender的subAppend()方法, 立即调用父类WriterAppender的subAppend()方法, 执行后再执行本类的滚动文件逻辑(该点在流程图第19步体现)。
14、在WriterAppender的subAppend()方法中调用layout执行内容格式化。
15、layout完成输出内容格式化, 返回内容信息。
16、调用QuietWriter将内容写入缓存。
17、在WriterAppender中, 判断属性是否为立即输出, 是则调用QuietWriter写出内容。
18、QuietWriter将内容写出到文件中(若appender为ConsoleAppender则是输出到控制台)。
19、回到RollingFileAppender的subAppend()方法中, 判断是否满足滚动文件逻辑, 是则执行rollOver()。
20、遍历完当前logger后, 判断当前logger的additive属性, 为false则不再递归父类, 提前结束流程。
例:
public class Main {public static void main(String[] args) {Logger logger = Logger.getLogger(Main.class);logger.debug("我爱你中国");}
}
一、 Logger logger = Logger.getLogger(Main.class);
1.1、跟进到LogManager的static代码块。
static {Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));repositorySelector = new DefaultRepositorySelector(h);String override = OptionConverter.getSystemProperty("log4j.defaultInitOverride", (String)null);if (override != null && !"false".equalsIgnoreCase(override)) {LogLog.debug("Default initialization of overridden by log4j.defaultInitOverrideproperty.");} else {String configurationOptionStr = OptionConverter.getSystemProperty("log4j.configuration", (String)null);String configuratorClassName = OptionConverter.getSystemProperty("log4j.configuratorClass", (String)null);URL url = null;if (configurationOptionStr == null) {url = Loader.getResource("log4j.xml");if (url == null) {url = Loader.getResource("log4j.properties");}} else {try {url = new URL(configurationOptionStr);} catch (MalformedURLException var7) {url = Loader.getResource(configurationOptionStr);}}if (url != null) {LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");try {OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());} catch (NoClassDefFoundError var6) {LogLog.warn("Error during default initialization", var6);}} else {LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");}}}
1.2、
1.2.1、初始化repositorySelector, 并设置选择器的LoggerRepository为Hierarchy, 创建根logger。
1.2.2、先查找log4j.xml, 没有再查找log4j.properties文件。
1.2.3、读取配置, 完成logger初始化 OptionConverter.selectAndConfigure(…);
1.3、跟进OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
public static void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {Configurator configurator = null;String filename = url.getFile();if (clazz == null && filename != null && filename.endsWith(".xml")) {clazz = "org.apache.log4j.xml.DOMConfigurator";}if (clazz != null) {LogLog.debug("Preferred configurator class: " + clazz);configurator = (Configurator)instantiateByClassName(clazz, Configurator.class, (Object)null);if (configurator == null) {LogLog.error("Could not instantiate configurator [" + clazz + "].");return;}} else {configurator = new PropertyConfigurator();}((Configurator)configurator).doConfigure(url, hierarchy);}
1.3.1、配置文件为log4j.xml, 则使用 DOMConfigurator来解析。
1.3.2、配置文件为log4j.properties, 则使用PropertyConfigurator来解析。
1.4、继续跟进((Configurator)configurator).doConfigure(url, hierarchy);到PropertyConfigurator类的doConfigure()方法。
public void doConfigure(Properties properties, LoggerRepository hierarchy) {this.repository = hierarchy;String value = properties.getProperty("log4j.debug");if (value == null) {value = properties.getProperty("log4j.configDebug");if (value != null) {LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");}}if (value != null) {LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));}String reset = properties.getProperty("log4j.reset");if (reset != null && OptionConverter.toBoolean(reset, false)) {hierarchy.resetConfiguration();}String thresholdStr = OptionConverter.findAndSubst("log4j.threshold", properties);if (thresholdStr != null) {hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr, Level.ALL));LogLog.debug("Hierarchy threshold set to [" + hierarchy.getThreshold() + "].");}this.configureRootCategory(properties, hierarchy);this.configureLoggerFactory(properties);this.parseCatsAndRenderers(properties, hierarchy);LogLog.debug("Finished configuring.");this.registry.clear();}
1.4.1、核心代码: 配置根logger及其appender。
this.configureRootCategory(properties, hierarchy);
void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) {LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");StringTokenizer st = new StringTokenizer(value, ",");if (!value.startsWith(",") && !value.equals("")) {if (!st.hasMoreTokens()) {return;}String levelStr = st.nextToken();LogLog.debug("Level token is [" + levelStr + "].");if (!"inherited".equalsIgnoreCase(levelStr) && !"null".equalsIgnoreCase(levelStr)) {logger.setLevel(OptionConverter.toLevel(levelStr, Level.DEBUG));} else if (loggerName.equals("root")) {LogLog.warn("The root logger cannot be set to null.");} else {logger.setLevel((Level)null);}LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());}logger.removeAllAppenders();//遍历log4j.rootLogger中的appenderNamewhile(st.hasMoreTokens()) {String appenderName = st.nextToken().trim();if (appenderName != null && !appenderName.equals(",")) {LogLog.debug("Parsing appender named \"" + appenderName + "\".");//核心代码:根据appenderName解析出appenderAppender appender = this.parseAppender(props, appenderName);if (appender != null) {//将appender加入到logger的AppenderAttachableImpl的appenderList中logger.addAppender(appender);}}}}
1.4.1.1、跟进logger.addAppender(appender);最终调用AppenderAttachableImpl类的addAppender方法。
public void addAppender(Appender newAppender) {if (newAppender != null) {if (this.appenderList == null) {this.appenderList = new Vector(1);}if (!this.appenderList.contains(newAppender)) {this.appenderList.addElement(newAppender);}}}
1.4.1.2、跟进Appender appender = this.parseAppender(props, appenderName);
Appender parseAppender(Properties props, String appenderName) {//根据appenderName获取registry缓存中的appender, 若存在则直接放回Appender appender = this.registryGet(appenderName);if (appender != null) {LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");return appender;} else {String prefix = "log4j.appender." + appenderName;//.layout在配置文件中是小写的String layoutPrefix = prefix + ".layout";appender = (Appender)OptionConverter.instantiateByKey(props, prefix, Appender.class, (Object)null);if (appender == null) {LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");return null;} else {appender.setName(appenderName);if (appender instanceof OptionHandler) {if (appender.requiresLayout()) {Layout layout = (Layout)OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, (Object)null);if (layout != null) {appender.setLayout(layout);LogLog.debug("Parsing layout options for \"" + appenderName + "\".");//设置layout属性PropertySetter.setProperties(layout, props, layoutPrefix + ".");LogLog.debug("End of parsing for \"" + appenderName + "\".");}}//初始化异常处理器String errorHandlerPrefix = prefix + ".errorhandler";String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);if (errorHandlerClass != null) {ErrorHandler eh = (ErrorHandler)OptionConverter.instantiateByKey(props, errorHandlerPrefix, ErrorHandler.class, (Object)null);if (eh != null) {appender.setErrorHandler(eh);LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\".");this.parseErrorHandler(eh, errorHandlerPrefix, props, this.repository);Properties edited = new Properties();String[] keys = new String[]{errorHandlerPrefix + "." + "root-ref", errorHandlerPrefix + "." + "logger-ref", errorHandlerPrefix + "." + "appender-ref"};Iterator iter = props.entrySet().iterator();while(true) {if (!iter.hasNext()) {PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\".");break;}Map.Entry entry = (Map.Entry)iter.next();int i;for(i = 0; i < keys.length && !keys[i].equals(entry.getKey()); ++i) {}if (i == keys.length) {edited.put(entry.getKey(), entry.getValue());}}}}//反射设置appender的其他属性PropertySetter.setProperties(appender, props, prefix + ".");LogLog.debug("Parsed \"" + appenderName + "\" options.");}this.parseAppenderFilters(props, appenderName, appender);//加入registry缓存中this.registryPut(appender);return appender;}}}
1.4.2、跟进this.configureLoggerFactory(properties);
protected void configureLoggerFactory(Properties props) {String factoryClassName = OptionConverter.findAndSubst("log4j.loggerFactory", props);if (factoryClassName != null) {LogLog.debug("Setting category factory to [" + factoryClassName + "].");this.loggerFactory = (LoggerFactory)OptionConverter.instantiateByClassName(factoryClassName, LoggerFactory.class, this.loggerFactory);PropertySetter.setProperties(this.loggerFactory, props, "log4j.factory.");}}
1.4.2.1、获取配置文件loggerFactory全限定名, 创建loggerFactory实例。
1.4.2.2、反射设置loggerFactory的属性,注: 不配置时, loggerFactory使用默认值, 为DefaultCategoryFactory实例对象. loggerFactory用于创建自定义的logger对象。
1.4.3、跟进this.parseCatsAndRenderers(properties, hierarchy);主要是父子logger的绑定和设置logger的additive属性。
protected void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {Enumeration enumeration = props.propertyNames();while(true) {while(enumeration.hasMoreElements()) {String key = (String)enumeration.nextElement();String loggerName;String value;if (!key.startsWith("log4j.category.") && !key.startsWith("log4j.logger.")) {if (key.startsWith("log4j.renderer.")) {loggerName = key.substring("log4j.renderer.".length());value = OptionConverter.findAndSubst(key, props);if (hierarchy instanceof RendererSupport) {RendererMap.addRenderer((RendererSupport)hierarchy, loggerName, value);}} else if (key.equals("log4j.throwableRenderer") && hierarchy instanceof ThrowableRendererSupport) {ThrowableRenderer tr = (ThrowableRenderer)OptionConverter.instantiateByKey(props, "log4j.throwableRenderer", ThrowableRenderer.class, (Object)null);if (tr == null) {LogLog.error("Could not instantiate throwableRenderer.");} else {PropertySetter setter = new PropertySetter(tr);setter.setProperties(props, "log4j.throwableRenderer.");((ThrowableRendererSupport)hierarchy).setThrowableRenderer(tr);}}} else {loggerName = null;if (key.startsWith("log4j.category.")) {loggerName = key.substring("log4j.category.".length());} else if (key.startsWith("log4j.logger.")) {loggerName = key.substring("log4j.logger.".length());}value = OptionConverter.findAndSubst(key, props);//核心代码: 创建出自定义logger对象, 绑定其与其他logger的关系Logger logger = hierarchy.getLogger(loggerName, this.loggerFactory);synchronized(logger) {this.parseCategory(props, logger, key, loggerName, value);this.parseAdditivityForLogger(props, logger, loggerName);}}}return;}}
二、logger.info(“我爱你中国”);
相关文章:

Log4j源码解析
Log4j源码解析 主要流程 Logger logger Logger.getLogger(Main.class); 1、通过Logger.getLogger(Class clazz) 或 Logger.getLogger(String name)进入。 2、加载LogManager进jvm, 执行静态代码块执行初始化, 创建出RepositorySelector实例及LoggerRepository实例(Hierarchy…...
Docker 容器访问宿主机服务
docker 网络简介 docker 在安装时会默认创建三个网络:bridge(默认网络模式)、 none 、host。 host 直接和宿主机共用网络。bridge 网络隔离,通过虚拟网桥(一般是 docker0)与宿主机通信。none 禁用网络功能…...
Go 发送邮件
要在Go中发送电子邮件,您可以使用第三方库,如 gomail 。以下是一个使用 gomail 发送电子邮件的示例代码: package main import ("fmt""gopkg.in/gomail.v2" ) func main() {// 创建邮件消息m : gomail.NewMessage()m.Se…...

Spring AOP 的概念及其作用
一、什么是 Spring AOP? 在介绍 Spring AOP 之前,首先要了解一下什么是 AOP ? AOP ( Aspect Oriented Programming ):面向切面编程,它是一种思想, 它是对某一类事情的集中处 理 。…...

python基础1——环境安装
文章目录 一、Windows安装二、Linux安装三、pycharm安装3.1 软件安装3.2 个性化设置3.3 基本使用3.3.1 定义变量3.3.2 查看数据类型3.3.3 运算符3.3.4 操作符3.3.5 转义符 一、Windows安装 1、下载软件安装包,官网 2、开始安装。 2.查看是否安装成功。 3.安装…...

uniapp 中 的progress加载进度条 的使用,在 页面显示数据加载的进度条,使用户的使用体验效果更好
学习目标: 学习目标如下: 例如: uniapp 中 的progress加载进度条 的使用,在 页面显示数据加载的进度条,使用户的使用体验效果更好 学习内容: 学习内容如下所示: 相关属性的说明 进度条的显…...

【尚硅谷】第01章:随堂复习与企业真题(Java语言概述)
来源:尚硅谷Java零基础全套视频教程(宋红康2023版,java入门自学必备) 基本都是宋老师发的资料里面的内容,只不过补充几个资料里没直接给出答案的问题的答案。 不想安装markdown笔记的app所以干脆在这里发一遍。 第01章:随堂复习…...
MyBatis的SqlSession理解
SqlSession是Mybatis最重要的构建之一,可以认为Mybatis一系列的配置目的是生成类似JDBC生成的Connection对象的statement对象,这样才能与数据库开启“沟通”,通过SqlSession可以实现增删改查(当然现在更加推荐是使用Mapper接口形式…...
axios 某个接口使用自己独有的完整地址
可以在axios请求中使用完整的URL,而不使用baseURL, 只需将url字段设置为完整的URL即可 import axios from axios;export function getInfo() {return axios({url: http://192.168.3.15:8086/test/messages,method: post}); }直接在url字段中提供了完整的…...

WEB:Web_python_template_injection
背景知识 python模板注入 ssit 题目 打开题目,发现页面提示,翻译为python模板注入 先测试是否存在注入 可以发现被执行了 先查看所有的子类 payload {{[].__class__.__base__.__subclasses__()}} 利用site.Printer的os模块执行命令 payload {{.__…...

【Android安全】Embedded Trace Microcell模块
ETM: Embedded Trace Macrocell, hardware unit responsible to generate hardware instruction trace. ETM模块用于在硬件层面实现instruction trace,可用于辅助逆向分析。 使用教程: https://mcuoneclipse.com/2016/11/05/tutorial-getting-etm-inst…...
修改内核驱动之后-如何给内核打补丁
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言思路步骤1.进入下面路径2.修改文件calibrate.c3.使用git工具生产补丁文件4.移动补丁文件到自己的Linux的recipem目录下总结前言 本文来学习如何使用YOCTO修改Linux内核驱动之后,如何通过打补…...

【javaSE】 类和对象详解
目录 面向对象的初步认知 什么是面向对象 面向对象与面向过程 类定义和使用 简单认识类 类的定义格式 注意事项 练习定义类 定义一个狗类 定义一个学生类 注意事项 类的实例化 什么是实例化 注意事项 类和对象的说明 this引用 为什么要有this引用 什么是this引…...

大数据课程D5——hadoop的Sink
文章作者邮箱:yugongshiyesina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握Sink的HDFS Sink; ⚪ 掌握Sink的Logger Sink; ⚪ 掌握Sink的File Roll Sink; ⚪ 掌握Sink的Null Sink; ⚪ 掌握Si…...

【数据结构】27.移除元素
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...
机器学习分布式框架ray运行xgboost实例
Ray是一个开源的分布式计算框架,专门用于构建高性能的机器学习和深度学习应用程序。它的目标是简化分布式计算的复杂性,使得用户能够轻松地将任务并行化并在多台机器上运行,以加速训练和推理的速度。Ray的主要特点包括支持分布式任务执行、Ac…...

C++设计模式笔记
设计模式 如何解决复杂性? 分解 核心思想:分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单的问题。 抽象 核心思想:从高层次角度讲,人们处理复杂性有一个通用的技术,及抽象。…...

简单聊聊创新与创造力
文章目录 前言一、大脑运行的两种方式1、聚焦模式2、发散模式3、影响想法的因素a、背景知识b、兴趣c、天赋 4、思维固化 二、想法的不可靠1、对想法进行验证2、颠覆性创新,挤牙膏式创新3、为什么模仿这么多 三、更多更多的idea1、个人的方面a、积累不同的背景知识b、…...

使用TensorFlow训练深度学习模型实战(上)
大家好,尽管大多数关于神经网络的文章都强调数学,而TensorFlow文档则强调使用现成数据集进行快速实现,但将这些资源应用于真实世界数据集是很有挑战性的,很难将数学概念和现成数据集与我的具体用例联系起来。本文旨在提供一个实用…...

【Spring】什么是Bean的生命周期及作用域,什么是Spring的执行流程?
博主简介:想进大厂的打工人博主主页:xyk:所属专栏: JavaEE进阶 在前面的播客中讲解了如何从Spring中存取Bean对象,那么本篇我们来讲解Bean对象的生命周期是什么,Bean对象的6种作用域分别是什么,都有哪些区别ÿ…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...