Log4j2官方文档翻译--架构

官网原文标题《Architecture》

翻译时间:2017-11-14

官网原文地址:http://logging.apache.org/log4j/2.x/manual/architecture.html

译者:本文介绍了log4j的主要构成组件和核心概念,并就每个组件分别进行了讲解。尤其需要读者重点理解的是log level的继承概念,以及appender的additivity属性。仔细理解本篇后,可继续学习配置的文章。

前序阅读《Welcome to Log4j 2!》

后续阅读:《Configuration》(还未更新)

 

架构

主要组件

log4j使用下图中所展示的class

使用Log4j2 API的程序需要向LogManager请求一个指定名称的Logger。LogManager会定位到合适的LoggerContext,然后从它获取logger。如果必须去创建logger,这会关联到包含下面因素的LoggerConfig a)同样名称的logger,b)父包的名称 或者c)root LoggerConfig。LoggerConfig对象通过配置中的Logger声明来创建。LoggerConfig会关联上appender,实际上由他负责传递LogEvent。

 

Logger的层级

任何超越System.out.println的日志API,最首要的优势就是它有能力使某些指定的日志代码片段失效,同时允许其他的正常打印。这种能力假定日志的空间,所有可能的日志片段的空间,基于开发者给出的条件进行分类。

在Log4j 1.x中,Logger 层级通过Logger之间的关系进行维护。在Log4j2中,这种关系不复存在。与之代替的,层级结构围护在LoggerConfig对象之间。

Logger和LoggerConfig是一组被命名的实体。Logger的命名是大小写敏感的,并且遵守层级命名规范:

命名层级

一个LoggerConfig可以被称为另外一个LoggerConfig的祖先,如果他的名字后面跟了一个点作为子孙logger名称的前缀。一个LoggerConfig可以称为孩子LoggerConfig的父亲,如果在他们之间没有任何祖先。

例如,一个loggerConfig称为"com.foo",是"com.foo.bar"这个logger的父亲。相似的,"java"是"java.util"的父亲,是"java.util.Vector"的祖先。这种命名的方式应该对于绝大多数开发者是很熟悉的。

root loggerConfig存在于LoggerConfig层级的顶层。它作为一个例外,它一直存在并且它是任何层级的一部分。直接关联到root LoggerConfig上的logger,可以按如下方式得到:

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

其实,还可以更为简单:

Logger logger = LogManager.getRootLogger();

所有其他的Logger也可以通过传递Logger名称给LogManager.getLogger静态方法被查到。更多关于Logging API的信息可以在访问Log4j 2 API.

 

LoggerContext

LoggerContext扮演着日志系统中的锚点角色。其实,我们可能在程序中有多个激活的LoggerContext,这取决于环境。关于LoggerContext更多的细节,在Log Separation 章节。

 

Configuration

每个LoggerContext都有一个激活的Configuration。这个configuration含有所有的Appender,context-wide Filter,LoggerConfig,并且含有StrSubstitutor的引用。在重新配置的过程中,会存在两个Configuration对象。一旦所有的Logger都重新关联到新的配置上,旧的configuraion就会被停掉并被抛弃。

 

Logger

像之前所说,Logger通过调用LogManager.getLogger来创建。Logger本身并不会执行任何直接的动作。他只是有个名称并且关联到LoggerConfig上。它继承于AbstractLogger ,实现了必要的方法。在当配置发生变化时,Logger可能被关联到另外的loggerConfig上。这会使得他们的行为被修改。

 

Retrieving Logger

通过传入同样名称,调用LogManager.getLogger,将会返回给你同样的Logger对象引用。

例如:

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

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

X和y关联到同一个logger对象上。

Log4j环境的配置一般会在程序初始化的时候完成。最佳的方式是读取配置文件。这些在 Configuration中讨论。

Log4j使得通过软件组件命名logger变得很容易。我们可以在class中通过初始化Logger来完成,使用class的全路径名称作为logger的名称。这是很有用,并且直接的定义logger的方式。当log输出产生日志的logger名称时,这种命名策略使得识别log消息的来源变得很简单。然而,这只是一个可能的,普通的,命名logger的策略。Log4j并不会限制,开发者可以自由命名。

用自己class的名称来命名logger是通用的习惯,所以LogManager.getLogger()非常的方便,可以自动使用class名称作为Logger的名称。

虽然如此,以logger所在的class来命名logger,看起来还是目前最好的策略。

 

LoggerConfig

日志配置中声明Logger的时候,LoggerConfig对象被创建。LoggerConfig包含了一组Filter,在LogEvent传递给任何的appender之前,他必须允许LogEvent通过。他含有一组用于处理event的appender引用。

 

Log Level

LoggerConfig会被指定Log Level。内置的Level包含 TRACE,DEBUG,INFO,WARN,ERROR,及FATAL。Log4j 2也支持客制化log level。另外一种得到更细粒度的机制是使用Markers 代替。

Log4j 1.x及Logback都有"Level继承" 的概念,在Log4j 2中,Logger及LoggerConfig是两个不同的对象。所以这个概念实现的不太一样。每个Logger关联到适合的LoggerConfig上,LoggerConfig可以反过来关联到他的父亲上,这可以达到同样的效果。

下面五张表中,展示了很多给定的level值,以及会造成的关联到每个Logger上的level。注意下面所有的情况,如果root LoggerConfig没有被配置,默认的层级将会被指定给它。

Logger 名称被分配的 LoggerConfigLoggerConfig LevelLogger Level
rootrootDEBUGDEBUG
XrootDEBUGDEBUG
X.YrootDEBUGDEBUG
X.Y.ZrootDEBUGDEBUG

在上面的例1中,只有root logger被配置了,并且有log level。所有其他的Logger关联到root LoggerConfig上,并且使用它的level

Logger 名称被分配的 LoggerConfigLoggerConfig LevelLogger Level
rootrootDEBUGDEBUG
XXERRORERROR
X.YX.YINFOINFO
X.Y.ZX.Y.ZWARNWARN

例2中,所有的logge都有配置的LoggerConfig,并且从它那里获取level

Logger 名称被分配的 LoggerConfigLoggerConfig LevelLogger Level
rootrootDEBUGDEBUG
XXERRORERROR
X.YXERRORERROR
X.Y.ZX.Y.ZWARNWARN

例3中,root、X、X.Y.Z分别有配置的同名LoggerConfig。X.Y Logger没有配置匹配名称的LoggerConfig,所以使用了X这个LoggerConfig,因为这个LoggerConfig的名称最长匹配了Logger的名称的开始。

Logger 名称被分配的 LoggerConfigLoggerConfig LevelLogger Level
rootrootDEBUGDEBUG
XXERRORERROR
X.YXERRORERROR
X.Y.ZXERRORERROR

例4中,root和X logger都有自己配置的同名LoggerConfig。X.Y、X.Y.Z没有配置的LoggerConfig,所以她们的level来自于分配给它们的X这个LoggerConfig。因为他的名称最长匹配了logger名称的开始

Logger 名称被分配的 LoggerConfigLoggerConfig LevelLogger Level
rootrootDEBUGDEBUG
XXERRORERROR
X.YX.YINFOINFO
X.YZX.YERRORERROR

例5中,root、X、X.Y都有各自配置好的同名LoggerConfig。X.YZ这个logger没有配置好的LoggerConfig,所以他的level来自于指定给他的loggerConfig X。因为X名称最长匹配了logger名称的开始。他并没有关联到X.Y这个loggertConfig,因为句点后的字符并没有精确匹配。

Logger 名称被分配的 LoggerConfigLoggerConfig LevelLogger Level
rootrootDEBUGDEBUG
XXERRORERROR
X.YX.Y ERROR
X.Y.ZX.Y ERROR

 

例6中,X.Y这个LoggerConfig没有配置level,所以他继承了X这个LoggerConfig的level。X.Y.Z这个logger使用X.Y这个LoggerConfig,因为他没有同名匹配的LoggerConfig。他也从LoggerConfig X那里继承了他的日志level。

下面的表格表明了Level过滤是如何工作的。表各种纵向表头是logEvent的level,横向的表头是关联了相应level的LoggerConfig。相交点指出了是否LogEvent允许被传递做进一步处理,还是被丢弃。

 

Event LevelLoggerConfig Level 
 TRACEDEBUGINFOWARNERRORFATALOFF
ALLYESYESYESYESYESYESNO
TRACEYESNONONONONONO
DEBUGYESYESNONONONONO
INFOYESYESYESNONONONO
WARNYESYESYESYESNONONO
ERRORYESYESYESYESYESNONO
FATALYESYESYESYESYESYESNO
OFFNONONONONONONO

 

Filter

在之前的章节,额外的自动日志过滤会如我们所想而发生,Log4j提供Filter可以应用在把控制传递给任何LoggerConfig之前。在控制传递给LoggerConfig之后但是调用任何appender之前,在控制传递给LoggerConfig之前,但是在调用指定的Apeender之前。方式上很像防火墙filter,每个filter可以返回三种结果,Accept、Deny、Neutral。Accept含义是其他的Filter都不用再调用了,event应该被处理。Deny意味着event要立即被忽略,控制返回给调用者。Neutral表明event要被传递给其他的Filter。如果这里没有其他的filter,那么event将被处理。

尽管event可能被filter接收,但是仍旧有可能不被记录。这可能发生在,当event被前置的LoggerConfig Filter所接受,但是被LoggerConfig的filter所拒绝或者被所有的appender所拒绝。

 

Appender

基于logger配置,选择性启用或者禁用日志请求的能力只是整体的一小部分。Log4j允许日志请求来记录日志到不同的目的地。在Log4j中的叫法,输出的目的地称为Appender。当前,Appender有console,file,remote scoket服务器,Apache Flume,JMS,remote UNIX Syslog daemon,还有很多种DB的API。学习 Appenders 章节来获取更多可用类型的信息。一个Logger可以分配不止一个Appender。

Appender可以被添加进logger,通过调用当前configuration的addLoggerAppender 方法。如果匹配Logger名称的LoggerConfig并不存在,那么会创建一个,Appender会被附上,之后所有Logger都会被通知更新他们的LoggerConfig引用。

对于给定的logger,任何启用的日志请求,都会被传送给这个Logger的LoggerConfig中所有的appender,同样传递给LoggerConfig的父亲的appender。换句话讲,Appender通过LoggerConfig的层级被追加继承。举个例子,如果console Appender被添加进root Logger,那么所有启用的日志请求都会至少在console中打印。如果一个额外的文件appender被添加到一个叫做C的LoggerConfig中,那么对于C及C的孩子,所有的可用的日志请求都会被记录到文件里及console中。我们也可以通过在配置文件中,关于Logger的声明部分设置addtivity="false",以此来重写默认的行为。

 

支配appender追加属性的规则总结如下:

Appender追加性

Logger L的日志输出会进入和L关联的LoggerConfig以及这个loggerConfig祖先的所有Appender。这就是appender追加性的含义。

然而,如果一个Logger L关联的LoggerConfig的祖先,称之为P吧,他的additivity标识被设为了false,之后L的输出将会直接给所有L的loggerConfig中的appender,也会给向上直到P的祖先的appender,包含P。但是并不会给P的任何祖先的appender。

Logger的additivity标识默认设为true

下表展示了一个例子:

Logger
Name
Added
Appenders
Additivity
Flag
Output TargetsComment
rootA1not applicableA1The root logger has no parent so additivity does not apply to it.
xA-x1, A-x2trueA1, A-x1, A-x2Appenders of "x" and root.
x.ynonetrueA1, A-x1, A-x2Appenders of "x" and root. It would not be typical to configure a Logger with no Appenders.
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1Appenders in "x.y.z", "x" and root.
securityA-secfalseA-secNo appender accumulation since the additivity flag is set to false.
security.accessnonetrueA-secOnly appenders of "security" because the additivity flag in "security" is set to false.

 

Layout

通常,用户想要客制化的不仅仅是输出的目的地,还有输出的格式。这可以通过给appender关联layout来达成。Layout负责根据用户希望的那样来格式化LogEvent,反之,appender负责把格式好的输出发送到目的地。PatternLayout,log4j标准发布版中的一部分,可以让用户指定输出的格式,通过类似C语言print函数中约定的pattern。

例如,使用转换pattern的PatternLayout "%r [%t] %-5p %c - %m%n" 将会输出类似下面的内容:

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

第一个字段是程序启动后,经过的毫秒数。第二个字段是产生日志的线程。第三个字段是日志片段的等级,第四个字段是logger关联的名称。‘-’后面的文本是消息片段。

Log4j 带有许多不同的Layout,用于各种情况,如JSON、XML、HTML及Syslog。其他database连接器appender,需要输入制定的字段,而不是普通的文本layout。

重要的是,log4j可以渲染 log消息的内容,通过用户指定的条件。例如,如果你需要频繁的记录Oranges,你当前项目中的一种对象类型。你可以创建Orangemessage,它可以接受Orange的实体并且把它传递给Log4j。这样在需要的时候,Orange对象可以被格式化适合的byte数组。

 

StrSubstitutor and StrLookup

StrSubstitutor 类 和 StrLookup 接口,其实是借自 Apache Commons Lang ,然后经过修改后支持LogEvent的求值。另外Interpolator class也是借自Apache Commons Configuration,来让StrSubstitutor通过各种StrLookup去查变量的值。他也被修改来支持logEvent求值。放在一起,它们提供了一个机制,允许相关变量的配置来自于System Properties,配置文件,ThreadContext Map,LogEvent中的StructuredData。在配置处理或者在每个event被处理的时候,如果组件有处理变量的能力,那么变量将被赋值。查看Lookups 获取更多信息。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值