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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ansible自动化运维(二)剧本、角色编写实战

&#x1f618;作者简介&#xff1a;一名运维工作人员。 &#x1f44a;宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。 &#x1f64f;创作不易&#xff0c;动动小…

【学习篇】SAE J1939协议—常用到的知识点

前言&#xff1a;以下关于SAE J1939协议知识点的学习均抄录自书籍&#xff0c;侵权请联系删除。 故障诊断 SAE J1939诊断应用层定义了用于诊断服务的报文帧&#xff0c;诊断报文&#xff08;DM&#xff09;提供了用于车辆进行诊断和维修的功能。 诊断故障代码定义 SAE J193…

C语言每日一题:5.至少是其他数字的两倍+两个数组的交集。

第一题&#xff1a;至少是两倍其他数字的最大数 第一题&#xff1a; 思路一&#xff1a; 1.需要我们返回最大数值的下标&#xff0c;所以先循环遍历我们的这个数组记录一下最大的数值和下标位置。 2.使用qsort排序&#xff08;总是存在唯一的最大整数&#xff09; 3所以排序之…

Java编程实现遍历两个MAC地址之间所有MAC的方法

Java编程实现遍历两个MAC地址之间所有MAC的方法 本文实例讲述了java编程实现遍历两个MAC地址之间所有MAC的方法。分享给大家供大http://家参考&#xff0c;具体如下&#xff1a; 在对发放的设备进行后台管理时,很多时候会用到设备MAC这个字段,它可以标识唯一一个设备。然而在数…

安全渗透--正则表达式

什么是正则表达式&#xff1f; 正则表达式是一组由字母和符号组成的特殊文本&#xff0c;它可以用来从文本中找出满足你想要的格式的句子。 一个正则表达式是一种从左到右匹配主体字符串的模式。 “Regular expression”这个词比较拗口&#xff0c;我们常使用缩写的术语“regex…

16K个大语言模型的进化树;81个在线可玩的AI游戏;AI提示工程的终极指南;音频Transformers课程 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; LLM 进化树升级版&#xff01;清晰展示 15821 个大语言模型的关系 这张进化图来自于论文 「On the Origin of LLMs: An Evolutionary …

uniapp 微信小程序 预览pdf方法

效果图&#xff1a; 1、在小程序中 // #ifdef MP */ 是区分运行的环境&#xff0c;在小程序中可使用如下方法uni.downloadFile({url: item.link,//文件地址success: function (res) {var filePath res.tempFilePath;uni.openDocument({filePath: filePath,showMenu: false…

spring 存储对象 + 获取对象

前言 本篇在spring中如何使用五大类注释与方法注释将对象加入IOC容器中&#xff0c;了解如何使用注释来获取容器中的Bean对象&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言1.通过注释将类加入IoC…

大数据Flink(五十):流式计算简介

文章目录 流式计算简介 一、数据的时效性 二、流式计算和批量计算

260. 只出现一次的数字 III

题目描述&#xff1a; 主要思路&#xff1a; 首先通过抑或的方式可以将所有两个的数字全部排除&#xff0c;得到两个单个数字的异或值。 接下来将当前得到的异或值取最低一位的1。 分析异或值的每一位&#xff0c;为1的肯定是两个数中一个有一个没有。于是可以通过这一特性将两…

【Java编程案例】面向对象实现模拟物流快递系统

文章目录 一、案例目标二、案例分析1. 交通工具类2. 保养接口3. 专用运输车类4. 定位功能接口5. 快递类 三、测试类四、总结 在现代社会&#xff0c;网购已经成为人们生活的重要组成部分。当用户在购物网站中下订单后&#xff0c;订单中的货物经过一系列的流程&#xff0c;最终…

2023十大最牛编程语言排行榜以及各语言的优缺点

文章目录 ⭐️ 2023年7月十大编程语言排行榜⭐️ 十大值得学习编程语言概要&#x1f31f; Python&#x1f31f; C/C&#x1f31f; Java&#x1f31f; C#&#x1f31f; JavaScript&#x1f31f; Swift&#x1f31f; Ruby&#x1f31f; GO&#xff08;Golang&#xff09;&#x1…

ElasticSearch Window Linux部署

文章目录 一、Window 集群部署二、Linux 单节点部署三、Linux 集群部署 一、Window 集群部署 创建 elasticsearch-cluster 文件夹&#xff0c;在内部复制三个elasticsearch服务 修改集群文件目录中每个节点的 config/elasticsearch.yml 配置文件 # -----------------------…

ROS 2 — 托管(生命周期)节点简介

一、说明 这篇文章是关于理解ROS 2中托管&#xff08;生命周期&#xff09;节点的概念。我们描述了概念性的想法以及我们为什么需要它。所以让我们开始吧&#xff01; 二、托管式节点 — 什么和为什么&#xff1f; 为了理解托管式节点&#xff0c;让我们从一个简单的问题陈述开…

LeetCode 918. Maximum Sum Circular Subarray【数组,动态规划】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

谷粒商城篇章5 ---- P173-P192 ---- 检索服务【分布式高级篇二】

目录 1 检索服务 1.1 搭建页面环境 1.1.1 引入依赖 1.1.2 将检索页面放到gulimall-search的src/main/resources/templates/目录下 1.1.3 调整搜索页面 1.1.4 将静态资源放到linux的nginx相关映射目录下/root/docker/nginx/html/static/ search/ 1.1.5 SwitchHosts配置域…

Zookeeper的基本概念以及安装

Zookeeper简介 Zookeeper是一个分布式的(多台机器同时干一件事情),开源的分布式应用程序协调服务,是Google公司Chubby产品,是Hadoop和Base重要的组件,.它是一个分布式应用程序提供一致性的服务的软件,提供的功能包括:配置服务,域名服务,分布式同步,组服务等 Zookeeper目…

大数据课程C4——ZooKeeper结构运行机制

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解Zookeeper的特点和节点信息&#xff1b; ⚪ 掌握Zookeeper的完全分布式安装 ⚪ 掌握Zookeeper的选举机制、ZAB协议、AVRO&#xff1b; 一、Zookeeper-简介 1. 特点…

TRT4-trt-integrate - 3 使用onnxruntime进行onnx的模型推理过程

前言&#xff1a; onnx是microsoft开发的一个中间格式&#xff0c;而onnxruntime简称ort是microsoft为onnx开发的推理引擎。允许使用onnx作为输入进行直接推理得到结果。 py接口的推理过程&#xff1a; main函数&#xff1a; if __name__ "__main__":session onn…

模拟量采集S_ITR函数(信捷C语言FC)

模拟量采集和转换函数非常简单,这里不再介绍,想了解具体算法的可以查看下面博客文章: PLC模拟量输入 模拟量转换FC S_ITR_博途模拟量转换程序_RXXW_Dor的博客-CSDN博客模拟量采集、工业现场应用特别广泛、大部分传感器的测量值和输出信号都是线型关系,所以我们可以利用线性…