日志框架 - Log4j 2

官网
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。

  1. 初始化 LoggerContextFactory,优先通过配置文件log4j2.component.properties,获取log4j2.loggerContextFactory对应的配置信息。log4j2.component.properties中所有的配置信息,都是通过单例模式的PropertiesUtil缓存的。
  2. 如果没有找到上述信息,则通过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 关键组成部分(局部)

参考资料:
https://www.jianshu.com/p/0c882ced0bf5

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>
···

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 在应用程序中添加日志记录总的来说基于三个目的:监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析...
    时待吾阅读 4,975评论 0 6
  • 在应用程序中添加日志记录总的来说基于三个目的:监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析...
    时待吾阅读 5,006评论 1 13
  • 作为Java开发人员,对于日志记录框架一定非常熟悉。而且几乎在所有应用里面,一定会用到各种各样的日志框架用来记录程...
    意识流丶阅读 13,919评论 0 13
  • 主要参考:https://blog.csdn.net/zwj1030711290/article/details/...
    TheTempest阅读 4,253评论 0 2
  • 在项目开发过程中,我们可以通过 debug 查找问题。而在线上环境我们查找问题只能通过打印日志的方式查找问题。因此...
    Java架构阅读 3,470评论 2 41