Log4j源码解析

news2024/11/26 1:42:48

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中的appenderName
        while(st.hasMoreTokens()) {
            String appenderName = st.nextToken().trim();
            if (appenderName != null && !appenderName.equals(",")) {
                LogLog.debug("Parsing appender named \"" + appenderName + "\".");
                //核心代码:根据appenderName解析出appender
                Appender 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.coloradmin.cn/o/794416.html

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

相关文章

13. linux指令

文章目录 一些linux的常用指令一、linux常用命令二、查看系统是32还是64位三、修改字符集四、Linux的时间问题1.查看和修改Linux的时区2.查看和修改Linux的时间 五、解压六、yum源配置1.yum介绍2.查看有无 yum安装包 &#xff0c;有的话卸载&#xff1a; 七、防火墙操作&#x…

达梦HSEM管理平台部署测试

目录 前期准备... 3 一、HSEM外部接入部署... 6 1、元数据库准备... 6 2、创建DmhsWebService. 6 3、启动DMHSWeb服务... 6 4、创建代理服务... 7 5、运行代理服务... 8 6、运行 Tomcat 服务... 8 7、登录HSEM管理平台... 8 二、Agent代理接入... 10 1、修改配置文件…

【VUE】npm打包报错 Syntax Error: Error: Cannot find module ‘imagemin-gifsicle‘

一. Syntax Error: Error: Cannot find module ‘imagemin-gifsicle’ npm run build 报错&#xff0c;报错如下 原因 这个错误消息显示缺少了 imagemin-gifsicle 模块&#xff0c;而它是 image-webpack-loader 的依赖项&#xff0c;导致构建失败。解决 &#xff08;1&#xf…

多值提取至点(样地因子提取)

1.导入因子tif&#xff0c;和样地表 2.值提取至点 输入对应的数据 也可以采用多值提取至点。 可以选择多个tif影像 提取后会将对应的字段添加在表后面 打开属性表可以看到 采用转换工具表转EXCEL可以导出为表格

【基础理论】隐马尔可夫模型及其算法

一、说明 根据L.R Rabiner等人[1]的说法&#xff0c;隐马尔可夫模型是一个双重嵌入的随机过程&#xff0c;其潜在的随机过程是不可观察的&#xff08;它是隐藏的&#xff09;&#xff0c;但只能通过另一组产生观察序列的随机过程来观察。 基本上&#xff0c;隐马尔可夫模型 &am…

机器学习之线性判别分析(Linear Discriminant Analysis)

1 线性判别分析介绍 1.1 什么是线性判别分析 线性判别分析&#xff08;Linear Discriminant Analysis&#xff0c;简称LDA&#xff09;是一种经典的监督学习算法&#xff0c;也称"Fisher 判别分析"。LDA在模式识别领域&#xff08;比如人脸识别&#xff0c;舰艇识别…

解决报错Avoid using non-primitive value as key, use string/number value instead.

找到图中画圈的文件这个错误信息的意思是要避免使用非基本值作为键&#xff0c;而是使用字符串/数字值代替。 [1] 这个错误通常出现在使用<el-select>中的<el-option>进行循环遍历值时。 [2] 这个错误的解决方案是检查是否有重复的键值&#xff0c;并确保使用字符…

C++ 引用(别名)

1.引用变量的定义&#xff1a; 类型&引用变量名称变量名称&#xff1b; &和类型结合称之为引用符号&#xff0c;不是取地址符&#xff0c;而是代表别名的意思。 #include<iostream> using namespace std; int main() {int a 10; //定义一个整型变量a并初始化为…

Java 实现提取富文本中包含特定字符串的图片 src 属性值

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

数据库触发器简介——修改数据的触发器、删除数据的触发器

1.修改数据的触发器 修改数据的触发器 create trigger tb_user_update_triggerafter update on tb_user for each row begininsert int user_logs(id,operation,operate_time,operate_id,operate_params)VALUES(null,update,now(),new.id,concat(更新之前的数据&#xff1a;i…

2023年的深度学习入门指南(20) - LLaMA 2模型解析

2023年的深度学习入门指南(20) - LLaMA 2模型解析 上一节我们把LLaMA 2的生成过程以及封装的过程的代码简单介绍了下。还差LLaMA 2的模型部分没有介绍。这一节我们就来介绍下LLaMA 2的模型部分。 这一部分需要一些深度神经网络的基础知识&#xff0c;不懂的话不用着急&#xf…

工厂方法模式——多态工厂的实现

1、简介 1.1、概述 在工厂方法模式中&#xff0c;不再提供一个统一的工厂类来创建所有的产品对象&#xff0c;而是针对不同的产品提供不同的工厂&#xff0c;系统提供一个与产品等级结构对应的工厂等级结构。 1.2、定义 工厂方法模式&#xff08;Factory Method Pattern&am…

与 ChatGPT 进行有效交互的几种策略

在这篇文章中&#xff0c;您将了解即时工程。尤其&#xff0c; 如何在提示中提供对响应影响最大的信息什么是角色、正面和负面提示、零样本提示等如何迭代使用提示来利用 ChatGPT 的对话性质 废话不多说直接开始吧&#xff01;&#xff01;&#xff01; 提示原则 快速工程是有…

跃焱邵隼网站demo

xdm 网站的代码开源了。 今年迷上摄影和剪辑了&#xff0c;所以很少投入到网站的维护。 然后经过群友的一些反馈&#xff0c;所以决定 将网站上demo开源放出来了。 后面有机会再出一些好玩的东西。 哦 对了 3d 编辑器我已经融入地图了 年底搞一些好玩的东西出来。 可以关注…

常用的镜像命令

docker images 列出本地主机上的镜像 各个选项说明: REPOSITORY&#xff1a;表示镜像的仓库源 TAG&#xff1a;镜像的标签版本号 IMAGE ID&#xff1a;镜像ID CREATED&#xff1a;镜像创建时间 SIZE&#xff1a;镜像大小 同一仓库源可以有多个 TAG版本&#xff0c;代表这个仓…

EtherNet/IP转 Modbus网关实现AB PLC控制变频器案例

捷米特JM-EIP-RTU网关 Modbus转ETHERNET/IP用于将多个变频器连接到Ethernet/Ip主网&#xff0c;以便森兰变频器可以由AB PLC控制。 配备专用于JM-EIP-RTU网关的EDS文件&#xff0c;AB PLC主站可以控制森兰逆变器从站。 使用 AB 系统的配置方法 1&#xff0c; 运行 RSLogix 500…

复习之linux存储的基本管理

一、实验环境的设定 1. 实验环境的搭建 本节内容只需要一台虚拟机 westosa reset重置虚拟机&#xff0c;保证实验环境的纯净配置网络实现ssh连接 重置虚拟机后&#xff0c;配置网络&#xff0c;设定ip&#xff1a;172.25.254.100&#xff0c;保证与主机可以通信 实现ssh连接…

代码随想录day13 | 226.翻转二叉树 101.对称二叉树

文章目录 一、翻转二叉树二、对称二叉树 一、翻转二叉树 使用前、后序反转最为方便。 // 使用递归三部曲 class Solution { public:TreeNode *dfs(TreeNode *root){// 前序反转if (root nullptr)return root;swap(root->left, root->right);dfs(root->left);dfs(ro…

三子棋(超详解+完整码源)

三子棋 前言一&#xff0c;游戏规则二&#xff0c;所需文件三&#xff0c;创建菜单四&#xff0c;游戏核心内容实现1.棋盘初始化1.棋盘展示3.玩家下棋4.电脑下棋5.游戏胜负判断6.game&#xff08;&#xff09;函数内部具体实现 四&#xff0c;游戏运行实操 前言 C语言实现三子棋…

TEE GP(Global Platform)认证规范

TEE之GP(Global Platform)认证汇总 一、GP认证规范库 二、TEE GP认证规范文档 如果需要TEE对应的GP认证规范文档&#xff0c;请按照下方选择框选择TEE&#xff0c;然后Search&#xff0c;共查询到31个相关规范文档。 参考&#xff1a; GlobalPlatform Certification - Global…