Log4j 2体系结构 - 概念理解
我们先依次看一下每个类的定义是什么。
LoggerContext
LoggerContext
作为日志系统的锚点。在一个应用中,有可能存在多个处于活跃状态的LoggerContext
。关于LoggerContext
的更多详细信息,请参考Log Separation。
The LoggerContext acts as the anchor point for the Logging system.
Configuration
Configuration
类对应的是正在使用的配置文件,例如log4j2.xml
。它包含了所有可用的Appender
、LoggerConfig
、以及Filter
的定义。
During reconfiguration two Configuration objects will exist. Once all Loggers have been redirected to the new Configuration, the old Configuration will be stopped and discarded.
在重新配置时(即配置文件有改动时),可能会并存两个Configuration
对象。一旦所有的Logger
都被重定向到新的配置,旧配置对应的Configuration
对象就会被废弃。
Logger
Logger
对象通过LogManager.getLogger
方式获得,一般每个类都有各自的Logger
对象,名称与该类的全量名一致。Logger
应该只是一个接口,本身不执行任何直接操作。,拥有名字并与一个LoggerConfig
相关联。
The Logger itself performs no direct actions. It simply has a name and is associated with a LoggerConfig.
Logger
的行为由与其关联的LoggerConfig
控制,当其与其他的LoggerConfig
相关联时,它的行为也会作相应的改变。
LoggerConfig
LoggerConfig
对象对应着配置文件(例如log4j2.xml
)中的
。它包含了一系列Filter
定义以及对一系列Appender
的引用。
LoggerConfig objects are created when Loggers are declared in the logging configuration.
Log4j 1.x与Log4j 2.x的不同在于,日志的层次结构发生了变化。在1.x中,日志的层次结构是通过Logger
之间的关系维护的,而在2.x中,是用LoggerConfig
对象之间的关系维护的。
In Log4j 1.x the Logger Hierarchy was maintained through a relationship between Loggers. In Log4j 2 this relationship no longer exists. Instead, the hierarchy is maintained in the relationship between LoggerConfig objects.
命名原则
在Log4j 2.x中,Logger
和LoggerConfig
会根据名称进行关联,而且它们的命名都满足如下的层次关系:
名为java
的LoggerConfig
是名为java.util
的LoggerConfig
父配置,是名为java.util.Vector
的LoggerConfig
的祖先配置。
反过来,名为java.util.Vector
的LoggerConfig
是名为java.util
的LoggerConfig
的子配置,是名为java
的LoggerConfig
的子孙配置。
A LoggerConfig is said to be an ancestor of another LoggerConfig if its name followed by a dot is a prefix of the descendant logger name. A LoggerConfig is said to be a parent of a child LoggerConfig if there are no ancestors between itself and the descendant LoggerConfig.
Root LoggerConfig
在整个LoggerConfig
体系的最顶层,它没有任何祖先配置。可以通过LogManager.getLogger(LogManager.ROOT_LOGGER_NAME)
或LogManager.getRootLogger()
的方式获得与Root LoggerConfig
相关联的Logger
。
日志等级
在Log4j 2.x中,每个LoggerConfig
都会被分配一个Log level
。Log4j 2.x内建支持的Log level
为:TRACE < DEBUG < INFO < WARN < ERROR < FATAL。除了这些内建等级,Log4j 2.x也允许用户去自定义等级(不过官方并不推荐这么做)。
除了等级机制,可以使用Marker
机制来实现更细粒度的控制,更多信息请参考[]()。
接下来,我们用下面5组实例来介绍一下Log4j 2.x中的Level Inheritance
(等级继承)机制。
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
root | root | DEBUG | DEBUG |
X | root | DEBUG | DEBUG |
X.Y | root | DEBUG | DEBUG |
X.Y.Z | root | DEBUG | DEBUG |
上表中,只有名为root的Logger
被赋予了名为root的LoggerConfig
且LoggerConfig
的等级为DEBUG,所以名为root的Logger
的等级也为DEBUG。其它的Logger
都没有特别分配LoggerConfig
,因此会继承它们的祖先配置root。
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
root | root | DEBUG | DEBUG |
X | X | ERROR | ERROR |
X.Y | X.Y | INFO | INFO |
X.Y.Z | X.Y.Z | WARN | WARN |
上表中,每个Logger
都与各自同名的LoggerCongfig
所关联,因此它们的等级都与相关联的LoggerConfig
的等级保持一致。
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
root | root | DEBUG | DEBUG |
X | X | ERROR | ERROR |
X.Y | X | ERROR | ERROR |
X.Y.Z | X.Y.Z | WARN | WARN |
上表中,唯独没有为名为X.Y的Logger
分配同名的LoggerConfig
,因此它将与名为X的LoggerConfig
相关联(父配置,而非是祖先配置root)。
Logger Name | Assigned LoggerConfig | LoggerConfig Level | Logger Level |
---|---|---|---|
root | root | DEBUG | DEBUG |
X | X | ERROR | ERROR |
X.Y | X.Y | INFO | INFO |
X.YZ | X | ERROR | ERROR |
从上表中可以看出,层次结构是以
.
作为分割的,而不是最长的名称匹配长度。所以X.YZ的父配置为X,而不是X.Y。
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 | ERROR |
上表中,虽然名为X.Y的Logger
与其同名LoggerCongfig
相关联,但未被赋予等级,将继承名为X的LoggerConfig
的等级ERROR。
下标展示了根据等级的过滤规则:
Event Level | LoggerConfig Level | |||||||
---|---|---|---|---|---|---|---|---|
- | TRACE | DEBUG | INFO | WARN | ERROR | FATAL | OFF | |
ALL | YES | YES | YES | YES | YES | YES | YES | |
TRACE | YES | NO | NO | NO | NO | NO | NO | |
DEBUG | YES | YES | NO | NO | NO | NO | NO | |
INFO | YES | YES | YES | NO | NO | NO | NO | |
WARN | YES | YES | YES | YES | NO | NO | NO | |
ERROR | YES | YES | YES | YES | YES | NO | NO | |
FATAL | YES | YES | YES | YES | YES | YES | NO | |
OFF | NO | NO | NO | NO | NO | NO | NO |
Appender
Appender
指日志输出的目的地。Log4j 2.x支持控制台、文件、远程socket服务器、Apache Flume、JMS、远程Unix Syslog守护进程和数据库API等作为日志输出的目的地。关于这些类型的详细信息,请参考Appenders。
In log4j speak, an output destination is called an Appender. An Appender can be added to a Logger by calling the addLoggerAppender method of the current Configuration. If a LoggerConfig matching the name of the Logger does not exist, one will be created, the Appender will be attached to it and then all Loggers will be notified to update their LoggerConfig references.
一般在配置文件(例如log4j2.xml
)中通过
来定义日志输出的目的地,并在
中将
与某个LoggerConfig
关联起来。如果要在程序中动态地为某个Logger
增加Appender
,可以调用Configuration
类的addLoggerAppender
方法。这将会把Appender
关联到与Logger
相匹配的LoggerConfig
对象中,如果LoggerConfig
对象不存在,则创建一个。然后所有的Logger
都会被通知到,以更新它们对LoggerConfig
的引用。
Appender
的相加性(Additivity)
Logger L
发起的日志请求将被送达到与L
相关联的LoggerConfig
中所引用的所有Appender
,以及那个LoggerConfg
所有的祖先配置中所引用的所有Appender
。这就是Appender
的相加性含义。
可以通过additivity=false
来控制LoggerConfig
的可加性。具体规则为,如果LoggerConfig P
的additivity
属性被设置为false
,任何与其关联或与其后代配置关联的Logger
发起的日志请求最多传递到P
,而不会送达到任何与P
的先配置相关联的Appender
。additivity
属性默认值为true
。
he output of a log statement of Logger L will go to all the Appenders in the LoggerConfig associated with L and the ancestors of that LoggerConfig. This is the meaning of the term "appender additivity".
However, if an ancestor of the LoggerConfig associated with Logger L, say P, has the additivity flag set to false, then L's output will be directed to all the appenders in L's LoggerConfig and it's ancestors up to and including P but not the Appenders in any of the ancestors of P.
Loggers have their additivity flag set to true by default.
为了方便理解Appender
的相加性,我们来举一个例子。如果控制台Console
被添加到与Root相匹配的LoggerConfig
(即Root LoggerConfig
),那么任何Logger
发起的日志请求都至少会在控制打印出来(如果它们没有修改过任何LoggerCongfig
的additivity
属性)。在此基础上,如果在某个LoggerConfig C
中添加一个文件类型的Appender
,与C
或C
的子孙配置相匹配的Logger
所发起的日志请求,都至少会在文件和控制台打印出来(如果没有修改任何LoggerConfig
的additivity
属性。
Logger Name | Added Appenders | Additivity Flag | Output Targets | Comments |
---|---|---|---|---|
Root | A1 | 无该属性 | A1 | Root没有父节点,所以无法设置additivity 属性 |
x | A-x1, A-x2 | true | A1, A-x1, A-x2 | 与x以及Root中添加的Appender |
x.y | none | true | A1, A-x1, A-x2 | x.y的祖先配置中的Appender |
x.y.z | A-xyz1 | true | A1, A-x1, A-x2, A-xyz1 | x.y.z的祖先配置(x和Root)中配置的Appender |
security | A-sec | false | A-sec | additivity 被设置为false 所以,不会送达到它的父配置(即Root)。 |
security.access | none | true | A-sec | 只能送达到它的父配置(security),而不能送达到Root |
Layout
Layout
用来定义每个Appender
输出的格式。关于Layout
的更多详细信息,请参考Layouts。
Log4j 2负责根据用户配置的Layout
渲染日志格式,然后Appender
负责将渲染后的日志内容发送到指定的目的地。
Filter
除了前面提到的基于级别的过滤机制,Log4j 2允许用户利用Filter
来进行更细粒度的控制。Filter
应用的时机总结为:
- 进入任何
LoggerConfig
之前 - 进入某个
LoggerConfig
之后,但在调用任何Appender
之前 - 进入某个
LoggerConfig
之后,在调用某个特定Appender
之前 - 在每个
Appender
上
Log4j 2中的Filter
机制类似于防火墙机制,每个Filter
的返回值范围为:接收、拒绝和中立。接收的意思为不需要再调用其他的Filter
,Log event
将被处理。拒绝的意思为Log event
将被立即忽略,调用返回到调用者那里。中立的意思指Log event
需要传递到其他Filter
。如果不存在其他的Filter
,那么Event log
将被处理。
尽管一个Log event
可以被某个Filter
接收,但它仍然可能不会被记录在日志中。因为,它可能会被后续的其他Filter
(例如LoggerConfig Filter
或Appender Filter
)所拒绝。
Although an event may be accepted by a Filter the event still might not be logged. This can happen when the event is accepted by the pre-LoggerConfig Filter but is then denied by a LoggerConfig filter or is denied by all Appenders.