vlambda博客
学习文章列表

深入理解Log4j 2.x概念与架构

深入理解Log4j 2.x概念与架构

摘要:本文主要学习Log4j 2.x概念与架构



为什么使用Log4j 2.x ?

Log4j 1.x 已被广泛采用并应用于许多领域。然而多年来它的发展已经放缓。需要维护与旧的Java版本兼容,使维护变得更加困难,并于2015年8月结束生命。SLF4J/Logback作为替代品,对框架进行了许多需要改进的地方,既然有替代品,为什么还要用使用Log4j 2?请读者仔细阅读以下使用的理由:

  1. Log4j 2被设计成可用作审计日志框架。Log4j 1.x和Logback在重新配置时会发生丢失事件,而Log4j 2不会。在Logback中,appender中的异常对应用程序不可见。在Log4j 2中,可以配置2个appender来允许异常渗透到应用程序中。

  2. Log4j 2包含基于LMAX Disruptor库的下一代异步记录器。在多线程的情况下,异步记录器的吞吐量是原来的10倍,比Log4j 1.x和Logback的延迟低。

  3. Log4j 2对于独立应用程序是无垃圾的,对于web应用程序是低垃圾的,可以减少垃圾收集器的压力,并可以提供更好的响应时间性能。

  4. Log4j 2使用了插件系统,通过添加新的附加程序、过滤器、布局、查找和模式转换器,而不需要任何更改。

  5. Log4j 2插件系统配置比较简单,配置中的条目不需要指定的类名。

  6. 支持自定义日志级别,自定义日志级别可以在代码或配置中定义。

  7. 支持lambda表达式,在Java8上运行的客户机代码可以使用lambda表达式,只有启用了请求的日志级别时,才惰性地构造日志消息。不需要显式级别检查,导致代码更干净。

  8. 支持消息对象。消息支持复杂的构造。用户可以自由创建消息类型,并编写自定义布局、进行筛选器和查找操作。

  9. Log4j 1.x支持附加程序上的过滤器。Logback添加了涡轮过滤器,允许过滤事件在被记录器处理之前。Log4j 2支持可以配置为在由记录器处理之前处理事件,就像由记录器处理或在附加物。

  10. 多数场景下Logback appender不接受布局,固定数据格式。而log4j2 appender接受布局,允许以任何格式传输数据。

  11. Log4j 1.x和Logback中的布局返回是字符串,需要编码器编码。Log4j 2采用更简单的方法,返回字节数组。这样做的好处是,它意味着它们实际上可以用于任何附加程序,而不仅仅是那些附加程序对输出流的写入。

  12. Syslog Appender同时支持TCP和UDP以及对BSD Syslog和RFC 5424格式。

  13. Log4j 2利用了Java 5的并发支持,并在最低级别执行锁,Log4j 1.x存在已知的死锁问题。其中许多是在Logback中修复的,但是许多Logback类仍然需要相当高级别的同步。



Log4j2.x架构

架构图



Logger Hierarchy (层次结构)

任何日志API与普通System.out.println相比,首要优势在于它能够禁用某些日志语句,同时允许其他日志语句不受阻。日志API能够对日志空间进行分类打印,这种能力成为了一些开发商选择的标准。

在Log4j 1.x中,记录器层次结构是通过记录器之间的关系来维护的。在Log4j中2这种记录器层次结构关系不再存在。相反,层次结构保持在LoggerConfig对象。Loggers和LoggerConfigs是命名实体。记录器名称区分大小写,并遵循分级命名规则。

命名层次结构:

一个LoggerConfig被称为另一个LoggerConfig的祖先,如果它的名字是子记录器名称的前缀,LoggerConfig被认为是父母。

例如,名为“com.foo”的LoggerConfig是名为“com.foo.Bar”的祖先。类似地,“java”是“java.util”和

“java.util.Vector”的父级。大多数开发人员都熟悉这个命名方案。根LoggerConfig位于LoggerConfig层次结构的顶部,它总是存在的且是每个层次的一部分,是根目录的记录器。

LoggerConfig可以获得如下信息:

Logger Logger=LogManager.getLogger(LogManager.ROOT_Logger_NAME);

或者,更简单地说:

Logger Logger=LogManager.getRootLogger();

通过传递的方式传递所需记录器的名称。

LoggerContext(记录器上下文)

LoggerContext是日志系统的定位点。但是有可能应用程序中的多个活动loggercontext,具体取决于环境。

Configuration(上下文配置)

每个LoggerContext都有一个活动配置,配置包含所有Appenders(附加程序),context-wide Filters(上下文范围的筛选器),LoggerConfigs包含对StrSubstitutor的引用。如果重新配置 两个存在的配置对象,Loggers 都被重定向到新配置,旧配置将被停止并丢弃。

Logger(记录器)

如前所述,通过调用LogManager.getLogger创建记录器。记录器本身不执行直接操作,只是有一个名称,并且与LoggerConfig相关联。Logger继承了AbstractLogger抽象记录器,并实现所需的方法。当配置被修改时,记录器可以与不同的LoggerConfig关联,从而导致其行为被修改。

LoggerConfig(记录器配置)

LoggerConfig对象是在日志配置中声明日志记录器时创建的。这个LoggerConfig包含一组Filters,将在LogEvent事件中传递给Appenders。它包含对应用于处理事件的Appenders集的引用。

Log Levels(日志级别)

内置日志级别:TRACE, DEBUG,INFO, WARN, ERROR, and FATAL,内置级别集包括跟踪、调试,信息、警告、错误和致命错误。Log4j 2还支持自定义日志级别。另一种机制为了获得更大的粒度,应该使用标记。Log4j 1.x和Logback都有“级别继承”的概念。在Log4j 2中,Loggers和LoggerConfigs是两个不同的对象,因此这个概念的实现方式不同。每个Logger引用相应的LoggerConfig,而LoggerConfig又可以引用其父级,从而实现同样的效果。

注意,在所有这些情况下,如果root LoggerConfig未配置默认值,级别将分配给它。


Logger Name Assigned LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y
ERROR
X.Y.Z X.Y.Z
ERROR

通俗的讲,Logger Name分为root和自定义的,如果自定义没有配置,就用root的级别,符合继承思想。继承思想中,如果子类覆盖了父类的就用子类自己定义的,如果未覆盖,就调用父类的,大概就是这个意思。

Filter(过滤器)

Log4j提供了可以在控件传递给任何LoggerConfig之前、之后应用的过滤器。跟防火墙过滤器非常相似,每个过滤器结果只能是接受、拒绝或中性三者之一。“接受”表示不应调用其他筛选器,事件应继续;“拒绝”意味着应该立即忽略该事件,并将控件返回给调用方;“中性”表示该事件应传递给其他筛选器。如果没有其他过滤器事件将被处理。虽然筛选器可以接受事件,但可能不记录该事件,因为可能发生当事件被预LoggerConfig筛选器接受,但是随后被LoggerConfig筛选器拒绝 或被所有追加者拒绝。


Appender(追加器)

Log4j允许将日志记录请求打印到多个目的地。在log4j speak中,输出目的地称为Appender(追加器)。目前Appender方式有:控制台、文件、远程套接字服务、Apache Flume、JMS、远程UNIX Syslog守护进程和各种数据库api。Appender可以通过调用addLoggerAppender方法追加一个Logger。如果与记录器名称匹配的LoggerConfig不存在,则将创建一个LoggerConfig,Appender附加到它,然后通知所有记录器更新其LoggerConfig.

Layout(布局)

通常,用户不仅希望自定义输出目的地,还希望自定义输出格式。布局负责根据用户的意愿格式化LogEvent,而Appender负责将Layout格式化输出发送到目标。PatternLayout是标准log4j发行版的一部分,它允许用户根据类似于C语言printf的转换模式指定输出格式。

例如,

转换模式:%r[%t]-5p%c-%m%n

PatternLayout输出

176 [main] INFO org.foo.Bar - Located nearest gas station.
--
第一个字段是程序启动后经过的毫秒数。
第二个字段是发出日志请求的线程。
第三个字段是日志语句的级别。
第四个字段是日志请求关联的记录器的名称。
“-”后面的文本是陈述。


Log4j为各种用例提供了许多不同的布局,如JSON、XML、HTML和系统日志(包括新的RFC 5424版本)。除此之外,appenders可以追加数据库连接器填充指定的字段而不是特定的文本布局。log4j将根据用户指定的内容呈现日志消息的内容。例如,如果经常需要记录桔子,则在当前project中创建一个OrangeMessage, Orange实例将其传递给Log4j,以便在需要时将橙色对象格式化为适当的字节数组。



Log4j 1.x 如何升级Log4j 2


Log4j 1.x 桥

也许最简单方法是用Log4j2 log4j-1.2-api.jar替换Log4j 1.x jar ;但是要成功使用此应用程序,必须满足以下条件

要求:

  1. 不能访问Log4j 1.x实现内部的方法和类,例如Appenders、LoggerRepository或Category的callAppenders方法。

  2. 不能以编程方式配置Log4j。

  3. 不能通过调用类DOMConfigurator或属性配置程序。

Log4j 2 API

在大多数情况下,从Log4j 1.x API到Log4j 2的转换应该相当简单。许多日志语句不需要修改。但是,如有必要,必须进行以下更改设置:

  1. 版本1中的主包是org.apache.log4j,版本2中是org.apache.logging.log4j日志

  2. 版本1中org.apache.log4j.Logger.getLogger() 修改为版本2中org.apache.logging.log4j.LogManager.getLogger()。

  3. 版本1 org.apache.log4j.Logger.getRootLogger()或org.apache.log4j.LogManager.getRootLogger() 修改为org.apache.logging.log4j.LogManager.getRootLogger()。

  4. 版本1org.apache.log4j.Logger.getLogger与org.apache.log4j.spi.LoggerFactory 必须

  5. 删除 log4j 2使用了另一个延伸机制。

  6. 版本1 org.apache.log4j.Logger.getEffectiveLevel()的调用替换为

  7. org.apache.logging.log4j.Logger.getLevel()。

  8. 版本1删除对org.apache.log4j.LogManager.shutdown()的调用, 版本2 启动时自动添加一个JVM关闭挂钩执行任何核心清理, 从Log4j 2.1开始,您可以指定一个自定义的ShutdownCallbackRegistry来覆盖默认的JVM关闭挂钩策略。从Log4j 2.6开始,您现在可以使org.apache.logging.log4j.LogManager.shutdown()启动关闭手动操作。

  9. 应用程序应该删除org.apache.log4j.Logger.setLevel()或类似方法在API中不受支持。 

  10. 应用程序应该转换为使用参数化消息而不是字符串串联。

  11. org.apache.log4j.MDC和org.apache.log4j.NDC已被ThreadContext替换 。



更多log4j2的知识,请参阅官网英文文献 https://logging.apache.org/log4j/2.x/articles.html ,作者会继续更新log4j2的知识。

大数据时代日志数据很重要,因为我们的大部分数据都来源日志数据,而非业务数据。