官网
http://logging.apache.org/log4j/2.x/index.html
理论篇
参考:
log4j,slf4j及Commons Logging介绍与原理使用
https://blog.csdn.net/honghailiang888/article/details/52681777
Log4j
Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务 器、NT的事件记录器、UNIX Syslog守护进程等;用户也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,用户能够更加细致地控制日志的生成过程。这些可以通过一个 配置文件来灵活地进行配置,而不需要修改程序代码。是 经典的一种日志解决方案。内部把日志系统抽象封装成Logger 、appender 、pattern 等实现。我们可以通过配置文件轻松的实现日志系统的管理和多样化配置。
LogBack
Logback是由log4j创始人设计的又一个开源日记组件。logback当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个 改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日记系统如log4j或JDK14 Logging。logback-access访问模块与Servlet容器集成提供通过Http来访问日记的功能。LOGBack 作为一个通用可靠、快速灵活的日志框架,将作为Log4j 的替代和SLF4J 组成新的日志系统的完整实现。官网上称具有极佳的性能,在关键路径上执行速度是log4j 的10 倍,且内存消耗更少。具体优势见:http://logback.qos.ch/reasonsToSwitch.html
Log4J vs. LogBack
LogBack作为一个通用可靠、快速灵活的日志框架,将作为Log4j的替代和SLF4J组成新的日志系统的完整实现。LOGBack声称具有极佳的性能,“ 某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在LogBack中需要3纳秒,而在Log4J中则需要30纳秒。 LogBack创建记录器(logger)的速度也更快:13微秒,而在Log4J中需要23微秒。更重要的是,它获取已存在的记录器只需94纳秒,而 Log4J需要2234纳秒,时间减少到了1/23。跟JUL相比的性能提高也是显著的”。
SLF4J
简单日记门面(Facade)SLF4J是为各种loging APIs提供一个简单统一的接口,从而使得最终用户能够在部署的时候配置自己希望的loging APIs实现。 Logging API实现既可以选择直接实现SLF4J接的loging APIs如: NLOG4J、SimpleLogger。也可以通过SLF4J提供的API实现来开发相应的适配器如Log4jLoggerAdapter、JDK14LoggerAdapter。
Apache Common-Logging
目前广泛使用的Java日志门面库。通过动态查找的机制,在程序运行时自动找出真正使用的日志库。但由于它使用了ClassLoader寻找和载入底层的日志库, 导致了象OSGI这样的框架无法正常工作,由于其不同的插件使用自己的ClassLoader。 OSGI的这种机制保证了插件互相独立,然而确使Apache Common-Logging无法工作。 apache最早提供的日志的门面接口。避免和具体的日志方案直接耦合。类似于JDBC 的api 接口,具体的的JDBC driver 实现由各数据库提供商实现。通过统一接口解耦,不过其内部也实现了一些简单日志方案。
SLF4J vs. Apache Common-Logging
SLF4J库类似于Apache Common-Logging。但是,他在编译时静态绑定真正的Log库。使用SLF4J时,如果你需要使用某一种日志实现,那么你必须选择正确的SLF4J的jar包的集合。 如此便可以在OSGI中使用了。
另外,SLF4J 支持参数化的log字符串,避免了之前为了减少字符串拼接的性能损耗而不得不写的if(logger.isDebugEnable()),现在你可以直接写:logger.debug(“current user is: {}”, user)。拼装消息被推迟到了它能够确定是不是要显示这条消息的时候,但是获取参数的代价并没有幸免。同时,日志中的参数若超过三个,则需要将参数以数组的形式传入,如:
Object[] params = {value1, value2, value3};
logger.debug(“first value: {}, second value: {} and third value: {}.”, params);
常用日志框架、集成方案介绍
参考:
jdk-logging、log4j、logback日志介绍及原理
https://yq.aliyun.com/articles/39036?spm=a2c4e.11153940.blogcont39037.26.24c77f5eqaved1
jcl与jul、log4j1、log4j2、logback的集成原理
https://yq.aliyun.com/articles/39037?spm=a2c4e.11153940.blogcont39038.24.2ea74f2cKtDUn7
slf4j与jul、log4j1、log4j2、logback的集成原理
https://yq.aliyun.com/articles/39038
slf4j、jcl、jul、log4j1、log4j2、logback大总结
https://yq.aliyun.com/articles/39039?spm=a2c4e.11153940.blogcont39038.25.2ea74f2cKtDUn7
Log4j 2 源码篇
参考资料:
log4j 源码浅析
http://zcty5v5.xyz/2017/03/01/source-code-log4j2/
log4j2源码分析
https://www.jianshu.com/p/0c882ced0bf5
log4j2 源码解析
https://bryantchang.github.io/categories/Log4j/
Log4j 2 关键组成部分(整体)
- LogManager :初始化指定/默认 LoggerContextFactory
- LoggerContextFactory: 工厂类,实例化 LoggerContext
- LoggerContext:日志上下文
- Logger:日志类
入口LogManager
是 是log4j的入口,主要属性是LoggerContextFactory。
- 初始化 LoggerContextFactory,优先通过配置文件log4j2.component.properties,获取log4j2.loggerContextFactory对应的配置信息。log4j2.component.properties中所有的配置信息,都是通过单例模式的PropertiesUtil缓存的。
- 如果没有找到上述信息,则通过ProviderUtil获取META-INF/log4j-provider.properties中的配置,并放入PROVIDERS 中(其实也是单例模式,采用的是懒初始化的方式);
log4j-provider.properties的内容如下:
LoggerContextFactory = org.apache.logging.log4j.core.impl.Log4jContextFactory
Log4jAPIVersion = 2.1.0
FactoryPriority= 10
默认情况下,是找不到log4j2.component.properties文件的,因此LogManager中的LoggerContextFactory,默认就是org.apache.logging.log4j.core.impl.Log4jContextFactory;
获取LoggerContext实例
Log4jContextFactory中有一个属性ContextSelector,默认是ClassLoaderContextSelector类型的对象,在Log4jContextFactory的构造方法中进行的初始化。
LogContext.start()调用逻辑:
LogContext.start()—>LogContext.reconfigure()—>LogContext.reconfigure(URI)
LogContext.reconfigure(URI)中,调用ConfigurationFactory.instance(),做了初始化工作,是对属性factories的初始化。
PluginManager用于管理log4j的插件类,是通过读取META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat,并按照category缓存起来的。在后面的逻辑中也会用到PluginManager。而这里是只获取category=CONFIGURATION_FACTORY_PROPERTY的插件信息,对应的列表如下:
yamlconfigurationfactory,org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory,YamlConfigurationFactory,false,false,configurationfactory
jsonconfigurationfactory,org.apache.logging.log4j.core.config.json.JsonConfigurationFactory,JsonConfigurationFactory,false,false,configurationfactory
propertiesconfigurationfactory,org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory,PropertiesConfigurationFactory,false,false,configurationfactory
xmlconfigurationfactory,org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory,XmlConfigurationFactory,false,false,configurationfactory
其实每个对应了一种格式的配置文件。
通过instance获取到ConfigurationFactory后,调用getConfiguration,会得到对应的配置信息,逻辑其实也很简单,其实就是尝试遍历上述列表中的factory,加载对应的配置信息,如果能通过factory找到对应的配置文件,则进行解析,最终生成对应的Configuration即可
解析配置信息
此时,已经得到对应的配置信息,接下来就是解析配置信息,其实就是再次解析上面得到的Configuration,转换成其他数据结构。用XmlConfigurationFactory举例,前面的逻辑得到的XmlConfiguration只是简单的配置信息,Element rootElement,在调用configuration.start后,会将rootElement转换为Node rootNode,node其实是一个树形结构,start逻辑过后,就形成了一颗完整的树状结构,每个node都有对应的一个Object对象。
我们最终获取到的Configuration就是由这一系列的对象构成的,也就是说完成了配置文件到java对象的转换!context的start逻辑也就结束了。
通过context获取logger
获取logger,其实就是为了获取loggerconfig,也就是对应了配置文件中的标签下的某一个标签对应的配置。当然这里也有缓存,是保存在LoggerContext.loggerRegistry中的。获取到了logger,也就得到了appenders,最终打印日志,也是通过这一系列appender输出的。
Log4j2 关键组成部分(局部)
log4j2中,有5个关键概念:
- Layout:布局,用于把LogEvent日志事件序列化成字节序列,不同-Layout实现具有不同的序列化方式.
- Manager:管理器,用于管理输出目的地,如:RollingFileManager用于管理文件滚动以及将字节序列写入到指定文件中.
- Appender:追加器,用于操作Layout和Manager,往单一目的地进行日志打印.
- LoggerConfig:日志配置,用于整合多个Appender,进行日志打印.
- Filter:过滤器,用于对LogEvent日志事件加以过滤,LoggerConfig和Appender都可以配置过滤器,也就是说日志事件会经过一总一分两层过滤.
配置文件基本元素与对象的映射关系
<Properties>
<Property>
<Loggers>
<Logger>
<Root>
<AppenderRef>
<Filters>
<Appenders>
属性占位符
sys
env
marker
jvmrunargs
bundle
java
log4j
date
sd
ctx
Logger 配置
additivity:日志可加性,如果配置为true,则在日志打印时,会通过Logger继承关系递归调用父Logger引用的Appender进行日志打印.
注意:该属性默认为true.在递归打印日志时,会忽略父Logger的level配置level:用于控制允许打印的日志级别上线,在配置示例中,只有级别<=info的LogEvent才会被放行,级别优先级顺序为OFF<FATAL<ERROR<WARN<INFO<DEBUG<TRACE<ALL
注意:level属性的配置时可选的,在获取level时会通过Logger继承关系递归获取,RootLogger的级别默认为error,其他默认为null.也就是说,如果全都不配置level的话,则所有Logger级别都默认为error.includeLocation:如果配置为true,则打印日志时可以附带日志点源码位置信息输出.同步日志上下文默认为true,异步默认为false.
LoggerConfig元素下可以单独配置Property元素,添加属性键值对,这些属性会在每次打印日志时,被追加到LogEvent的contextData中LoggerConfig支持配置过滤器,在判断是否打印日志时,先过滤器判断过滤,然后再级别判断过滤.
AppenderRef:顾名思义,就是配置当前Logger引用的Appender.同时,AppenderRef也支持配置level和Filter,进行更细粒度的日志过滤
LoggerConfig等于总开关,AppenderRef则为各个子开关,两个开关都通过才能打印日志
Logger继承机制
Appender
<Console>
<File>
<RollingFile>
<RollingRandomAccessFile>
<Kafka>
<Failover>
<Socket>
Layout
PatternLayout
JsonLayout
···
Manager
Filter
过滤器的核心职责就是对LogEvent日志事件进行匹配,匹配结果分为匹配和不匹配,结果值有3种:接受,拒绝,中立.可由用户自定义匹配和不匹配的行为结果.
<ThresholdFilter>
···