Log4j 2.x架构

主要组件

Log4j 2中所使用的类如下图所示。ImportNew注:如果你对Log4j 2不了解,建议阅读这篇文章《Log4j 2 介绍》。

使用Log4J 2 API的程序可以根据特定名称向LogManager请求Logger。LogManager将会定位到合适的LoggerContext,然后从中取得Logger。如果需要创建Logger,创建过程会关联到一个特定的LoggerConfig,这个LoggerConfig的名字a)与Logger相同,b)与Logger所属包的名字相同,c)与根层LoggerConfig名字相同。LoggerConfig对象根据配置中的Logger声明创建而成,与传输LogEvent的Appender关联在一起。

Logger层级

相比于一般的System.out.println, 任何日志API首要的优点在于它能够禁用特定的日志语句同时还不影响其它的打印功能。这种功能假设的前提是日志空间,即所有可能的日志语句的空间,按照开发者选定的标准来分类。

Logger 层级在Log4j 1.x中通过Logger之间的关系来维护。在Log4j 2中 不再使用这种关系,替代它的是通过LoggerConfig对象之间的关系来维护Logger层级。

Loggers 和 LoggerConfigs 是带有名字的实体。Logger名字对大小写敏感,遵循下列层级命名规则:

命名层级

如果一个LoggerConfig的名字加点号(.)是另一个LoggerConfig名字的前缀,那么前一个LoggerConfig就是后一个LoggerConfig 的祖先。如果一个LoggerConfig和它的子层之间没有其他祖先,那么就称它是这个子层的父亲。

例如,名字为“com.foo”的LoggerConfig是名字为“com.foo.Bar”的LoggerConfig的父层。类似地,”Java”是”Java.util”的父层同时还是“java.util.Vector”的祖先层。大多数的开发人员应该很熟悉这种命名框架。

根层LoggerConfig处于LoggerConfig层级的顶端。它的独特之处在于一直存在而且是每个层级的一部分。与根层LoggerConfig关联的Logger可以这样获得:

Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

其它的Logger都可以通过将期望的Logger的名字传入LogManager.getLogger静态方法来获取。可以从Log4j 2 API获取更多日志API的信息。

LoggerContext

LoggerContext在日志系统中起锚的作用。根据具体情况,程序中有可能使用多个激活的LoggerContext。 从Log Separation查看关于LoggerContext的详细信息。

Configuration

每个LoggerContext有一个活动的Configuration与之对应。这个Configuration包含多有的Appender、上下文 Filter、LoggerConfig还包括对StrSubstitutor的引用。在重新配置过程中,会有两个Configuration对象同时存在。一旦所有Loggers重定向到新的Configuration,旧的Configuration就会停止和丢弃。

Logger

正如前文所述,Logger通过调用LogManager.getLogger 创建。Logger本身不执行直接动作,仅仅只有一个名字与一个LoggerConfig关联,继承AbstractLogger 并实现了所需的方法。当Configuration改变时,Logger有可能关联到另一个不同的LoggerConfig,如此一来,就会导致它们的行为发生变化。

获得Logger

使用相同的名字调用getLogger方法总是返回同一个Logger对象的引用。

例如,

Logger x = Logger.getLogger("wombat");
Logger y = Logger.getLogger("wombat");

x 和 y 完全指向同一个Logger对象。

Log4j环境配置在程序初始化时就已经完成。 首选的配置方式是读取配置文件。 具体信息在Configuration中讨论。

借助Log4j可以很容易使用组件来给Logger命名。在每个类中使用此类的全名来实例化一个Logger就可以实现这个目的。这种定Logger的方法既有用又直观明了。由于输出的日志包含创建Logger所用的名字,所以通过这种命名策略很容易就区分出日志信息的来源。虽然这种方式很常用,但这只是给Logger命名的一种方式。Log4j对Logger的设置没有任何限制。程序员可以按照自己的方式来给Logger命名。

然而,根据Logger所在类的名字来命名看起来是目前为止最好的方式。

LoggerConfig

在logging配置中声明Logger后,LoggerConfig对象就被创建出来,其中包含一组过滤器,LogEvent在传入任何Appander之前会经过这些过滤器。LoggerConfig还包含了Appender集合的引用,这些Appander用来处理事件。

日志等级

LoggerConfigs 会指定一个日志等级。等级集合包括(TRACE、DEBUG、INFO、WARN、ERROR和FATAL)。需要注意的是,在Log4j2中,Level是枚举类型不能被继承。对于那些对日志粒度更高的用户,建议采用Marker。

Log4j 1.x和Logback都有“层级继承”的概念。在Log4j 2中,由于Logger和LoggerConfig是两个不同的对象,因此“层级继承”的实现也有所不同。每个Logger引用适当的LoggerConfig,后者可以依次引用它的父层,从而达到相同的效果。

下面5个表格包括了各种等级(Level)值和与记录器(Logger)相关联的结果等级(resulting level)。需要注意的是,对于所有这些情况如果跟层LoggerConfig没有进行设置,那么会指定一个默认等级。

Logger名称 指定的LoggerConfig 等级
root root DEBUG
X root DEBUG
X.Y root DEBUG
X.Y.Z root DEBUG

上面的例1只对root logger配置并指定Log等级。其它的Logger都引用此 root LoggerConfig并使用这个Log等级。

Logger名称 指定的LoggerConfig 等级
root root DEBUG
X X ERROR
X.Y X.Y INFO
X.Y.Z X.Y.Z WARN

例2中,所有的logger都配置了LoggerConfig,分别在这些LoggerConfig中设置了各自的Log等级。

Logger名称 指定的LoggerConfig 等级
root root DEBUG
X X ERROR
X.Y X ERROR
X.Y.Z X.Y.Z WARN

例3中,root、X 和 X.Y.Z每个都配置了相同名字的LoggerConfig。Logger X.Y没有与其名字相匹配的LoggerConfig,之所以使用 LoggerConfig X作为配置,因为这个LoggerConfig的名字(X )是Logger名字(X.Y)的起始最长匹配。

Logger 名称 指定的LoggerConfig 等级
root root DEBUG
X X ERROR
X.Y X ERROR
X.Y.Z X ERROR

例4中,记录器(Logger) root and X 都配置了相同名字的LoggerConfig。而记录器(Logger) X.Y 和 X.Y.Z 与其名字相匹配的LoggerConfig,之所以从LoggerConfig X 中获取自己等级,因为这个LoggerConfig的名字(X )是这两个记录器(Logger)名字(X.Y和X.Y.Z )的起始最长匹配。

Logger 名称 指定的LoggerConfig 等级
root root DEBUG
X X ERROR
X.Y X.Y INFO
X.YZ X ERROR

例5中,记录器(Logger)root.X, X.Y  都有相同名字的LoggerConfig。而记录器(Logger) X.YZ 与其名字相匹配的LoggerConfig,之所以从LoggerConfig X 中获取自己等级,因为这个LoggerConfig的名字(X )是它的名字(X.YZ)的起始最长匹配。没有使用LoggerConfig X.Y 作为配置,是因为点之后(向右)的匹配必须准确无误。

接下来的表格阐述Lever过滤器如何工作。竖直方向抬头Event等级,水平方向抬头是与适当LoggerConfig相关的等级。交点表示LogEvent是否继续接受处理(Yes)还是丢弃(No)。

Event Level

LoggerConfig Level

TRACE

DEBUG

INFO

WARN

ERROR

FATAL

ALL

NO

NO

NO

NO

NO

NO

TRACE

YES

NO

NO

NO

NO

NO

DEBUG

YES

YES

NO

NO

NO

NO

INFO

YES

YES

YES

NO

NO

NO

WARN

YES

YES

YES

YES

NO

NO

ERROR

YES

YES

YES

YES

YES

NO

FATAL

YES

YES

YES

YES

YES

YES

OFF

YES

YES

YES

YES

YES

YES

Filter

前面描述了自动日志等级过滤如何进行,除此之外Log4j还提供了其它的过滤器,这些过滤器可以在下面情况下使用到 :控制权交给任何LoggerConfig之前,控制权交给LoggerConfig之后任何Appender调用之前,控制权交给LoggerConfig之后任何Appender调用之前以及每个Appender调用的时候。 工作方式与防火墙过滤器非常类似,有三种返回值:接受、拒绝或者中立 ,每个过滤器返回其中之一。接受表示表示不应该再调用其他的过滤器并且继续处理该事件。拒绝表示时间立即忽略该事件并且把控制权交还给调用者。中立表示应该把该事件传递个其他过滤器。如果没有别的过滤器可用,就将其处理。

尽管事件能被过滤器接收,但也可能没有记录下来。当之前的LoggerConfig过滤器接受了 一个事件,而LoggerConfig或所有的Appender拒绝了此事件,这种情况就会发生。

Appender

基于Logger能够选择性地启用或禁止日志请求只是Appender蓝图的一部分。Log4j允许日志请求打印到多个目标,Log4j中的Appender正是所谓的输出目标。目前,Appender已应用于控制台、文件、远程套接字服务器、Apache Flume、JMS以及远程UNIX系统日志守护进程。多个Appender可以依附到同一个过滤器中。

通过调用当前配置的 addLoggerAppender方法可以将Appender加入Logger中。如果没有与Logger的名字相匹配的LoggerConfig存在创建一个,Appender就依附在这个LoggerConfig上,然后通知所有的Logger更新他们引用的LoggerConfig。

对于给定的Logger,每个活动日志请求都会交给这个Logger的LoggerConfig以及它父层的所有appender。也就是说,Appender是从LoggerConfig层级相加继承而来。例如,如果在根层Logger中加入控制台appender,那么所有活动日志请求至少都会打印到控制台。在此基础上,如果在名字为C的LoggerConfig加入文件appender,那么对C以及C的子层所有活动日志请求都会打印到文件和控制台。通过在配置文件的Logger声明里设置additivity=”false”可以改变默认行为,从而使得Appender积累不再具备可加性。

控制appender可加性的规则总结如下:

Appender 可加性

记录器(Logger)L日志语句将输出到L所关联的LoggerConfig以及这个LoggerConfig祖先的所有Appender。这就是“appender可加性”的含义。但是,如果L所关联的LoggerConfig的祖先P的可加性标志设置为false,那么L将直接输出到L所关联的LoggerConfig以及其祖先一直到P的所有appender,但不会输出到P的祖先的任何Appender。

Logger的可加性标志默认为 true

下表可以作为一个示例:                       

Logger名称 添加的Appenders Additivity标志 输出目标 注释
root A1 not applicable 不可用 A1 The root logger has no parent so additivity does not apply to it. 根层Logger没有父层,因此没有可加性。
x A-x1, A-x2 true A1, A-x1, A-x2 Appenders of “x” and root “x” 根层root 的 Apender.
x.y none true A1, A-x1, A-x2 Appenders of “x” and root. “x” 和root的Appender. 定义了Logger却不指定Appender,这种情况比较少见。
x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1 Appenders of “x.y.z”, ”x” and root “x.y.z”, “x” 和 根层的Appender.
security A-sec false A-sec No appender accumulation since the additivity flag is set to false. 由于additivity标识符为false,所以没有Appender
security.access none true A-sec Only appenders of “security” because the additivity flag in “security” is set tofalse. 由于“security”的additivity标志位false,所以只有”security”的Appender

Layout

通常,用户期望定制的不仅仅是输出目标还有输出格式。将Layout关联到Appender可是达到这个期望。布局负责根据用户的期望来格式化LogEvent,而Appender负责将格式化结果输出到目标。布局模式PatternLayout是标准log4j发布包的一部分,它允许用户根据转换模式来指定输出格式 ,这种转换模式与C语言的 printf方法相似。

例如,布局模式”%r [%t] %-5p %c – %m%n” 的 输出类似于:

176 [main] INFO  org.foo.Bar - Located nearest gas station.

第一个字段是从程序启动开始所经过的毫秒数。第二个字创建该日志请求的线程。第三个字段是日志语句的等级。第二个字段是此日志请求所关联Logger的名字。‘-’之后的文本是输出语句的信息。

同样重要的是,log4j可以根据用户指定的标准来呈现日志信息的内容。例如,如果你经常需要记录当前项目中的对象类型Oranges,就可以创建接受Orange实例的OrangeMessage 类型,并将其传给Log4J,Orange对象在需要时就可以格式化成适当的字节数组。

StrSubstitutor 和 StrLookup

StrSubstitutor类和 StrLookup接口借用自Apache Commons Lang 经过修改得以支持LogEvents. 另外Interpolator 类借用自Apache Commons Configuration,它允许StrSubstitutor估算来自多个StrLookups的变量的值。Interpolator 同样经过修改以支持LogEvent。 它们结合起来提供了一种允许配置引用变量的机制,这些变量可以来自系统属性变量、配置文件、ThreadContext Map、LogEvent中的结构数据。当处理配置文件或处理每个事件时,如果组件具备解析功能就解析这些变量。      

英文原文:Apache.org,编译:ImportNew - 滕开选

译文连接:http://www.importnew.com/3180.html

【如需转载,请在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】

关于作者: 滕开选

"只用一样东西,不明白它的道理,实在不高明",做一个严谨的码农。出自上海大学数学系,在寻求数学与计算机完美结合的道路上,渐行渐远。现在就职于Hexagon Metrology,从事研发工作。 新浪微博: 枯鰧小樹

查看滕开选的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部