log4j2源码分析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j2源码分析相关的知识,希望对你有一定的参考价值。

参考技术A

1.概述
1.1.组件概览
1.2.灵活的配置
1.2.1.插件发现机制
1.2.2.插件装配机制
1.2.3.配置文件基本元素与对象的映射关系
2.属性占位符
2.1.概述
2.2.Interpolator插值器
2.3.默认属性配置
3.Logger
3.1.配置示例
3.2.配置详解
3.3.Logger继承机制
4.Appender
4.1.概述
4.2.框架支持的Appender实现
4.3.常用Appender详解
4.3.1.ConsoleAppender
4.3.2.RollingFileAppender
5.Layout
5.1.概述
5.2.PatternLayout
5.2.1.模式字符串
6.Manager
7.Filter

在log4j2中,LogManager就是日志的门面,相当于slf4j-api中的LoggerFactory.
框架为每个类加载分配了一个单独的LoggerContext,用于管理所有创建出来的Logger实例.
ContextSelector则负责管理类加载器到对应的LoggerContext实例之间的映射关系.
log4j2中,有5个关键概念:

组件架构如下:

在log4j2中,一切皆插件,框架通过 PluginRegistry 扫描并发现插件配置.

PluginRegistry 支持两种扫描方式

插件配置以 PluginType 的形式保存在插件注册表中, PluginType 的作用类似于spring中 BeanDefinition ,定义了如何创建插件实例.
插件类通过 @PluginFactory 注解或者 @PluginBuilderFactory 注解配置插件实例的实例化和属性注入方式.

log4j2知道如何实例化插件后,我们就可以通过编写配置文件(如:log4j2.xml),进行插件的实例化和属性注入了.
Configuration 全局配置对象负责保存所有解析到的配置.
通过 ConfigurationFactory.getConfiguration() 可以使用不同的工厂生产不同的配置对象,不同的 Configuration 实现可以解析不同格式的配置,如:xml,yaml,json等.

以xml文件为例,文件中每个元素都会最终对应一个插件实例,元素名称实际就是PluginType中的name,实例的属性可以从子元素对应的实例获取,也可以从自身元素的属性配置获取.

因此,xml中dom树的元素嵌套关系,也就是log4j组件实例的引用嵌套关系.

xml,yaml,json格式文件都可以描述这种嵌套关系,因此log4j2中定义了与文件格式无关的数据结构,Node来抽象配置.

AbstractConfiguration.setup() 负责提取配置,形成Node树.
AbstractConfiguration.doConfigure() 负责根据Node树,进行插件实例化和属性注入.

在log4j2中,环境变量信息(键值对)被封装为StrLookup对象,该对象作用类似于spring框架中的PropertySource.

在配置文件中,基本上所有的值的配置都可以通过参数占位符引用环境变量信息,格式为:$prefix:key.

Interpolator内部以Map<String,StrLookup>的方式,封装了很多StrLookuo对象,key则对应参数占位符$prefix:key中的prefix.

同时,Interpolator内部还保存着一个没有prefix的StrLookup实例,被称作默认查找器,它的键值对数据来自于log4j2.xml配置文件中的<Properties>元素的配置.

当参数占位符$prefix:key带有prefix前缀时,Interpolator会从指定prefix对应的StrLookup实例中进行key查询,

当参数占位符$key没有prefix时,Interpolator则会从默认查找器中进行查询.

Interpolator中默认支持的StrLookup查找方式如下(StrLookup查找器实现类均在org.apache.logging.log4j.core.lookup包下):

注意:Properties元素一定要配置在最前面,否则不生效.

log4j2框架会根据LoggerConfig的name建立对象之间的继承关系.这种继承机制与java的package很像,name以点进行名称空间分割,子名称空间继承父名称空间.
名称空间可以是全限定类名,也可以是报名.整个配置树的根节点就是RootLogger.
举例:假如我们的配置的Logger如下:

当通过LogManager.getLogger(name)获取Logger实例时,会根据name逐级递归直到找到匹配的LoggerConfig,或者递归到Root根节点为止.

追加器,负责控制Layout进行LogEvent的序列化,以及控制Manager对序列化后的字节序列进行输出.

在log4j2.xml配置文件中,配置方式如下:

控制台追加器,用于把日志输出到控制台,一般本地调试时使用.
配置示例如下:

文件滚动追加器,用于向本地磁盘文件中追加日志,同时可以通过触发策略(TriggeringPolicy)和滚动策略(RolloverStrategy)控制日志文件的分片,避免日志文件过大.
线上环境常用.

常用的触发策略包含两种:

滚动策略的实现包含两种:

配置示例如下:

布局对象,职责是把指定的LogEvent转换成可序列化对象(如:String),或者直接序列化成字节数组.

log4j2支持很多的序列化格式,如:普通模式字符串,JSON字符串,yaml字符串,XML格式字符串,html字符串等等.

类体系如下:

模式布局是我们最常使用的,它通过PatternProcessor模式解析器,对模式字符串进行解析,得到一个List<PatternConverter>转换器列表和List<FormattingInfo>格式信息列表.

在PatternLayout序列化时,会遍历每个PatternConverter,从LogEvent中取不同的值进行序列化输出.

模式字符串由3部分组成,格式为:%(格式信息)(转换器名称)选项1选项2...

模式字符串的格式为:
%-(minLength).-(maxLength)(转换器名称)选项字符串
minLength代表字段的最小长度限制,当字段内容长度小于最小限制时,会进行空格填充.
minLength前面的-负责控制对齐方式,默认为右对齐(左边空格填充),如果加上-,则会切换为左对齐方式(右边空格填充)
maxLength代表字段的最大长度限制,当字段内容长度大于最大限制时,会进行内容阶段
maxLength前面的-负责控制阶段方向,默认为左侧阶段,如果加上-,则会切换为右侧阶段
minLength和maxLength之间用点分隔.
格式信息中所有属性都是可选的,不配置,则使用默认值

log4j2会通过 PluginManager 收集所有类别为Converter的插件,同时分析插件类上的 @ConverterKeys 注解,获取转换器名称,并建立名称到插件实例的映射关系.
PatternParser识别到转换器名称的时候,会查找映射.

框架支持的所有转换器如下:

有时我们需要对特定的转换器进行特殊的配置,如:给DatePatternConverter配置时间格式,这个时候需要通过选项字符串配置.
PatternParser会提取模式字符串中的所有选项,保存在一个List<String>中,每个包裹的内容作为一个选项.
当创建转换器时,框架会自动扫描转换器类中声明的静态工厂方法newInstance,同时支持两种可选的形参,一种是Configuration,另一种String[]则会注入选项列表.
选项列表的识别由不同的转换器各自定义.

最后,以一个实际的例子解释配置:
日志会输出时间,类名,方法名,消息以及一个换行符.
同时,我们给DatePatternConverter指定了了时间格式,并且限制全限定类名最小长度为5,右截断,最大为10,左对齐.

管理器的职责主要是控制目标输出流,以及把保存在ByteBuffer字节缓冲区中的日志序列化结果,输出到目标流中.
如:RollingFileManager需要在每次追加日志之前,进行滚动检查,如果触发滚动还会创建新的文件输出流.
manager继承体系如下:

过滤器的核心职责就是对 LogEvent 日志事件进行匹配,匹配结果分为匹配和不匹配,结果值有3种:接受,拒绝,中立.可由用户自定义匹配和不匹配的行为结果.

所有实现了 Filterable 接口的组件都可以引用一个过滤器进行事件过滤,包含 LoggerConfig 和 AppenderControl 等.

框架实现的过滤器如下:

以上是关于log4j2源码分析的主要内容,如果未能解决你的问题,请参考以下文章

log4j2使用详解

深入源码解析日志框架Log4j2

v73.02 鸿蒙内核源码分析(参考手册) | 阅读内核源码必备工具 | 百篇博客分析OpenHarmony源码

Mesos源码分析

Mybatis源码分析

Spring源码分析专题——目录