此文为读log4j2 user guaid时的翻译及笔记。log4j2与log4j在Logger的继承关系和配置方式上都作出了修改。我的感受比较有意思的是Logger对象与 LoggerConfig解耦的设计,以及Filter中的传递机制,有点像网络包分发,不过多了不少可调控性。java
log4j2能够按照开发人员预先的设定,在指定的位置和状况下打印log语句,而且能够酌情关闭某些log语句,如开发阶段debug类型的语句等。而且,可使用layout来定义输出语句的格式,像C语言的printf函数同样。如:服务器
要实现这样标准化的日志输出,只须要在工程中引入log4j2的相关jar包,并向LogManager对象申请一个Logger对象的引用,而后调用该对象的相应方法便可,如:网络
在log4j2中,一共有五种log level,分别为TRACE, DEBUG,INFO, WARN, ERROR 以及FATAL。运维
FATAL:用在极端的情形中,即必须立刻得到注意的状况。这个程度的错误一般须要触发运维工程师的寻呼机。socket
ERROR:显示一个错误,或一个通用的错误状况,但还不至于会将系统挂起。这种程度的错误通常会触发邮件的发送,将消息发送到alert list中,运维人员能够在文档中记录这个bug并提交。函数
WARN:不必定是一个bug,可是有人可能会想要知道这一状况。若是有人在读log文件,他们一般会但愿读到系统出现的任何警告。spa
INFO:用于基本的、高层次的诊断信息。在长时间运行的代码段开始运行及结束运行时应该产生消息,以便知道如今系统在干什么。可是这样的信息不宜太过频繁。线程
DEBUG:用于协助低层次的调试。翻译
TRACE:用于展示程序执行的轨迹。debug
经过类图可用看到:
每个log上下文对应一个configuration,configuration中详细描述了log系统的各个 LoggerConfig、Appender(输出目的地)、EventLog过滤器等。每个Logger又与一个LoggerConfig相关联。另 外,能够看到Filter的种类不少,有聚合在Configuration中的filter、有聚合在LoggerConfig中的filter也有聚合 在Appender中的filter。不一样的filter在过滤LogEvent时的行为和判断依据是不一样的,具体可参加本文后面给出的文档。
应用程序经过调用log4j2的API并传入一个特定的名称来向LogManager请求一个Logger实例。LogManager会定位到适当的 LoggerContext 而后经过它得到一个Logger。若是LogManager不得不新建一个Logger,那么这个被新建的Logger将与LoggerConfig相关联,这个LoggerConfig的名称中包含以下信息中的一种:①与Logger名称相同的②父logger的名称③ root 。当一个LoggerConfig的名称与一个Logger的名称能够彻底匹配时,Logger将会选择这个LoggerConfig做为本身的配置。若是不能彻底匹配,那么Logger将按照最长匹配串来选择本身所对应的LoggerConfig。LoggerConfig对象是根据配置文件来建立的。LoggerConfig会与Appenders相关联,Appenders用来决定一个log request将被打印到那个目的地中,可选的打印目的地不少,如console、文件、远程socket server等。。LogEvent是由Appenders来实际传递到最终输出目的地的,而在EvenLog到达最终被处理以前,还须要通过若干filter的过滤,用来判断该EventLog应该在何处被转发、何处被驳回、何处被执行。
Logger间的层次关系
相比于纯粹的System.out.println方式,使用logging API的最首要以及最重要的优点是能够在禁用一些log语句块的同时容许其余的语句块的输出。这一能力创建在一种假设之上,即全部在应用中可能出现的logging语句能够按照开发者定义的标准分红不一样的类型。
在 Log4j 1.x版本时,Logger的层次是靠Logger类之间的关系来维护的。但在Log4j2中, Logger的层次则是靠LoggerConfig对象之间的关系来维护的。
Logger和LoggerConfig均是有名称的实体。Logger的命名是大小写敏感的,而且服从以下的分层命名规则。(与java包的层级关系相似)。例如:com.foo是com.foo.Bar的父级;java是java.util的父级,是java.util.vector的祖先。
root LoggerConfig位于LoggerConfig层级关系的最顶层。它将永远存在与任何LoggerConfig层次中。任何一个但愿与root LoggerConfig相关联的Logger能够经过以下方式得到:
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
其余的Logger实例能够调用LogManager.getLogger 静态方法并传入想要获得的Logger的名称来得到。
LoggerContext
LoggerContext在Logging System中扮演了锚点的角色。根据状况的不一样,一个应用可能同时存在于多个有效的LoggerContext中。在同一LoggerContext下,log system是互通的。如:Standalone Application、Web Applications、Java EE Applications、"Shared" Web Applications 和REST Service Containers,就是不一样广度范围的log上下文环境。
Configuration
每个LoggerContext都有一个有效的Configuration。Configuration包含了全部的Appenders、上下文范围内的过滤器、LoggerConfigs以及StrSubstitutor.的引用。在重配置期间,新与旧的Configuration将同时存在。当全部的Logger对象都被重定向到新的Configuration对象后,旧的Configuration对象将被停用和丢弃。
Logger
如前面所述, Loggers 是经过调用LogManager.getLogger方法得到的。Logger对象自己并不实行任何实际的动做。它只是拥有一个name 以及与一个LoggerConfig相关联。它继承了AbstractLogger类并实现了所需的方法。当Configuration改变时,Logger将会与另外的LoggerConfig相关联,从而改变这个Logger的行为。
得到Logger:
使用相同的名称参数来调用getLogger方法将得到来自同一个Logger的引用。如:
Logger x = Logger.getLogger("wombat");
Logger y = Logger.getLogger("wombat");
x和y指向的是同一个Logger对象。
log4j环境的配置是在应用的启动阶段完成的。优先进行的方式是经过读取配置文件来完成。
log4j使采用类名(包括完整路径)来定义Logger 名变得很容易。这是一个颇有用且很直接的Logger命名方式。使用这种方式命名能够很容易的定位这个log message产生的类的位置。固然,log4j也支持任意string的命名方式以知足开发者的须要。不过,使用类名来定义Logger名仍然是最为推崇的一种Logger命名方式。
LoggerConfig
当Logger在configuration中被描述时,LoggerConfig对象将被建立。LoggerConfig包含了一组过滤器。LogEvent在被传往Appender以前将先通过这些过滤器。过滤器中包含了一组Appender的引用。Appender则是用来处理这些LogEvent的。
Log层次:
每个LoggerConfig会被指定一个Log层次。可用的Log层次包括TRACE, DEBUG,INFO, WARN, ERROR 以及FATAL。须要注意的是,在log4j2中,Log的层次是一个Enum型变量,是不能继承或者修改的。若是但愿得到跟多的分割粒度,可用考虑使用Markers来替代。
在Log4j 1.x 和Logback 中都有“层次继承”这么个概念。可是在log4j2中,因为Logger和LoggerConfig是两种不一样的对象,所以“层次继承”的概念实现起来跟Log4j 1.x 和Logback不一样。具体状况下面的五个例子。
例子一:
可用看到,应用中的LoggerConfig只有root这一种。所以,对于全部的Logger而言,都只能与该LoggerConfig相关联而没有别的选择。
例子二:
在例子二中能够看到,有5种不一样的LoggerConfig存在于应用中,而每个Logger都被与最匹配的LoggerConfig相关联着,而且拥有不一样的Log Level。
例子三:
能够看到Logger root、X、X.Y.Z都找到了与各类名称相同的LoggerConfig。而LoggerX.Y没有与其名称相彻底相同的LoggerConfig。怎么办呢?它最后选择了X做为它的LoggerConfig,由于X LoggerConfig拥有与其最长的匹配度。
例子四:
能够看到,如今应用中有两个配置好的LoggerConfig:root和X。而Logger有四个:root、X、X.Y、X.Y.Z。其中,root和X都能找到彻底匹配的LoggerConfig,而X.Y和X.Y.Z则没有彻底匹配的LoggerConfig,那么它们将选择哪一个LoggerConfig做为本身的LoggerConfig呢?由图上可知,它们都选择了X而不是root做为本身的LoggerConfig,由于在名称上,X拥有最长的匹配度。
例子五
能够看到,如今应用中有三个配置好的LoggerConfig,分别为:root、X、X.Y。同时,有四个Logger,分别为:root、X、X.Y以及X.YZ。其中,名字能彻底匹配的是root、X、X.Y。那么剩下的X.YZ应该匹配X仍是匹配X.Y呢?答案是X。由于匹配是按照标记点(即“.”)来进行的,只有两个标记点之间的字串彻底匹配才算,不然将取上一段彻底匹配的字串的长度做为最终匹配长度。
Filter
与防火墙过滤的规则类似,log4j2的过滤器也将返回三类状态:Accept(接受), Deny(拒绝) 或Neutral(中立)。其中,Accept意味着不用再调用其余过滤器了,这个LogEvent将被执行;Deny意味着立刻忽略这个event,并将此event的控制权交还给过滤器的调用者;Neutral则意味着这个event应该传递给别的过滤器,若是再没有别的过滤器能够传递了,那么就由如今这个过滤器来处理。
Appender
由logger的不一样来决定一个logging request是被禁用仍是启用只是log4j2的情景之一。log4j2还容许将logging request中log信息打印到不一样的目的地中。在log4j2的世界里,不一样的输出位置被称为Appender。目前,Appender能够是console、文件、远程socket服务器、Apache Flume、JMS以及远程 UNIX 系统日志守护进程。一个Logger能够绑定多个不一样的Appender。
能够调用当前Configuration的addLoggerAppender函数来为一个Logger增长。若是不存在一个与Logger名称相对应的LoggerConfig,那么相应的LoggerConfig将被建立,而且新增长的Appender将被添加到此新建的LoggerConfig中。尔后,全部的Loggers将会被通知更新本身的LoggerConfig引用(PS:一个Logger的LoggerConfig引用是根据名称的匹配长度来决定的,当新的LoggerConfig被建立后,会引起一轮配对洗牌)。
在某一个Logger中被启用的logging request将被转发到该Logger相关联的的全部Appenders上,而且还会被转发到LoggerConfig的父级的Appenders上。
这样会产生一连串的遗传效应。例如,对LoggerConfig B来讲,它的父级为A,A的父级为root。若是在root中定义了一个Appender为console,那么全部启用了的logging request都会在console中打印出来。另外,若是LoggerConfig A定义了一个文件做为Appender,那么使用LoggerConfig A和LoggerConfig B的logger 的logging request都会在该文件中打印,而且同时在console中打印。
若是想避免这种遗传效应的话,能够在configuration文件中作以下设置:
additivity="false"
这样,就能够关闭Appender的遗传效应了。具体解释见:
Layout
一般,用户不止但愿能定义log输出的位置,还但愿能够定义输出的格式。这就能够经过将Appender与一个layout相关联来实现。Log4j中定义了一种相似C语言printf函数的打印格式,如"%r [%t] %-5p %c - %m%n" 格式在真实环境下会打印相似以下的信息:
176 [main] INFO org.foo.Bar - Located nearest gas station.
其中,各个字段的含义分别是:
%r 指的是程序运行至输出这句话所通过的时间(以毫秒为单位);
%t 指的是发起这一log request的线程;
%c 指的是log的level;
%m 指的是log request语句携带的message。
%n 为换行符。