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(“我爱你中国”);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/10170.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何提高自动化测试覆盖率

实施自动化测试最重要的就是要保证其可用性&#xff0c;而不少同学写了不少自动化测试用例&#xff0c;但感觉到其可用性不高。究其原因&#xff0c;不是自动化测试本身的问题&#xff0c;是实施自动化测试的时候没有考虑周全。 第一&#xff0c;不合事宜地引入自动化测试 在公…

轻量级Web报表工具ActiveReportsJS全新发布v4.0,支持集成更多前端框架!

ActiveReportsJS 是一款基于 JavaScript 和 HTML5 的轻量级Web报表工具&#xff0c;采用拖拽式设计模式&#xff0c;不需任何服务器和组件支持&#xff0c;即可在 Mac、Linux 和 Windows 操作系统中&#xff0c;设计多种类型的报表。ActiveReportsJS 同时提供跨平台报表设计、纯…

将Python远控隐藏在文档图片中的行动分析

1、概述 ** **近日&#xff0c;安天CERT通过网络安全监测发现了一起恶意文档释放Python编写的远控木马事件。通过文档内容中涉及的组织信息和其中攻击者设置的诱导提示&#xff0c;安天CERT判断该事件是一起针对阿塞拜疆共和国国家石油公司进行的定向攻击活动。此次事件中&…

13、ffmpeg使用nvidia显卡对OAK深度相机进行解码和编码

基本思想&#xff1a;简单使用nvidia的硬件解码进行oak相机的编码和解码学习 一、在本机rtx3060配置好显卡驱动和cuda之后进行下面操作50、ubuntu18.04&20.04CUDA11.1cudnn11.3TensorRT7.2/8.6Deepsteam5.1vulkan环境搭建和YOLO5部署_ubuntu18.04安装vulkan_sxj731533730的…

Redis 基础知识和核心概念解析:探索 Redis 的数据结构与存储方式

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Python-Go python模块与包 - GO问题 - GO容量测试

目录 go 模块与包复习&#xff08;Init函数 - go mod&#xff09; init函数有什么用&#xff1f;init函数是什么&#xff1f; go.mod文件是什么&#xff1f;有什么作用&#xff1f; python的模块与包 python中包的分类 1、内置模块&#xff1a;不需要安装&#xff0c;直接…

前端工作中常用 CSS 知识点整理

1.1文字溢出省略号 文字单行溢出: overflow: hidden; // 溢出隐藏 text-overflow: ellipsis; // 溢出用省略号显示 white-space: nowrap; // 规定段落中的文本不进行换行 多行文字溢出: overflow: hidden; // 溢出隐藏 text-overflow: …

Java电子招投标采购系统源码-适合于招标代理、政府采购、企业采购

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看…

【简单认识MySQL主从复制与读写分离】

文章目录 一、MySQL主从复制1、配置主从复制的原因&#xff1a;2、主从复制原理1、 MySQL的复制类型2、 MySQL主从复制的工作过程;1、 MySQL主从复制延迟2、优化方案&#xff1a;3、 MySQL 有几种同步方式&#xff1a; 三种4、异步复制&#xff08;Async Replication&#xff0…

【宝藏系列】20个常用的Python技巧

【宝藏系列】20个常用的Python技巧 文章目录 【宝藏系列】20个常用的Python技巧&#x1f349;文末推荐【Python之光】 Python的可读性和简单性是其广受欢迎的两大原因&#xff0c;本文介绍20个常用的Python技巧来提高代码的可读性&#xff0c;并能帮助你节省大量时间&#xff0…

springboot整合ELK+kafka采集日志

一、背景介绍 在分布式的项目中&#xff0c;各功能模块产生的日志比较分散&#xff0c;同时为满足性能要求&#xff0c;同一个微服务会集群化部署&#xff0c;当某一次业务报错后&#xff0c;如果不能确定产生的节点&#xff0c;那么只能逐个节点去查看日志文件&#xff1b;lo…

工业自动化数据485采集网关支持modbus协议

S475/S475E系列是一种功能丰富的设备&#xff0c;支持多种通信方式和输入输出功能。以下是对其功能的详细介绍&#xff1a; 通信方式&#xff1a; S475/S475E系列支持多种通信方式&#xff0c;包括短信、RS485、语音拨号、GPRS/3G/4G无线数据网络四种告警方式。这意味着用户可…

AXI协议之AXILite开发设计(二)

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 二、AXI-Lite关键代码分析 1、时钟与…

营销系统积分数据库设计

营销系统总体数据-业务功能模型 在当今日益竞争的市场中&#xff0c;如何提高客户留存率和忠诚度&#xff0c;已成为各大企业迫切需要解决的问题。而积分商城/系统作为一种新型的营销方式&#xff0c;受到青睐。 积分商城/系统是指将用户在使用产品或服务时产生的积分&#xf…

X - Transformer

回顾 Transformer 的发展 Transformer 最初是作为机器翻译的序列到序列模型提出的&#xff0c;而后来的研究表明&#xff0c;基于 Transformer 的预训练模型&#xff08;PTM&#xff09; 在各项任务中都有最优的表现。因此&#xff0c;Transformer 已成为 NLP 领域的首选架构&…

mac m1 触控栏TouchBar功能栏异常

电脑可能在高温下运行时间过长&#xff0c;导致TouchBar之前正常显示的调整屏幕亮度与调整声音等功能的按钮均丢失&#xff0c;然后看了一眼键盘设置&#xff0c;设置也是正常的&#xff0c;已勾选显示功能栏 下面请看 如何在MacBook Pro&#xff08;macOS Monterey&#xff0…

基于Java+SpringBoot+vue前后端分离社区医院信息平台设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

【C++】智能指针

文章目录 1. 为什么需要智能指针&#xff1f;2. 智能指针的使用智能指针的常见问题1.使用对象的生命周期去控制资源2. 像指针一样使用3. 拷贝问题auto_ptr ——管理权转移unique_ptr ——防拷贝C98版本C11版本 shared_ptr (根本解决拷贝问题)赋值代码实现 weak_ptr —— 循环引…

RocketMQ教程-(4)-领域模型-消费者(Consumer)

本文介绍 Apache RocketMQ 中消费者&#xff08;Consumer&#xff09;的定义、模型关系、内部属性、行为约束、版本兼容性及使用建议。 定义​ 消费者是 Apache RocketMQ 中用来接收并处理消息的运行实体。 消费者通常被集成在业务系统中&#xff0c;从 Apache RocketMQ 服务…

【JVM】详解对象的创建过程

文章目录 1、创建对像的几种方式1、new关键字2、反射3、clone4、反序列化 2、创建过程步骤 1、检查类是否已经被加载步骤 2、 为对象分配内存空间1、指针碰撞针对指针碰撞线程不安全&#xff0c;有两种方案&#xff1a; 2、空闲列表选择哪种分配方式 步骤3、将内存空间初始化为…