博客园博问闪存首页新随笔联系管理订阅 随笔 153 文章 0 评论 41 Log4j架构及概念简介
log4j——Log for java。此文为读log4j2 user guaid时的翻译及笔记。个人感觉比较有意思的是Logger对象与LoggerConfig解耦的设计,以及Filter中的传递机制,有点像网络包分发,不过多了很多可调控性。
前言log4j2可以按照开发人员预先的设定,在指定的位置和情况下打印log语句,并且可以酌情关闭某些log语句,如开发阶段debug类型的语句等。并且,可以使用layout来定义输出语句的格式,像C语言的printf函数一样。如:
要实现这样标准化的日志输出,只需要在工程中引入log4j2的相关jar包,并向LogManager对象申请一个Logger对象的引用,然后调用该对象的相应方法即可,如:
在log4j2中,一共有五种log level,分别为 TRACE, DEBUG, INFO, WARN, ERROR 以及 FATAL 。
FATAL :用在极端的情形中,即必须马上获得注意的情况。这个程度的错误通常需要触发运维工程师的寻呼机。 ERROR :显示一个错误,或一个通用的错误情况,但还不至于会将系统挂起。这种程度的错误一般会触发邮件的发送,将消息发送到 alert list 中,运维人员可以在文档中记录这个 bug 并提交。 WARN :不一定是一个 bug ,但是有人可能会想要知道这一情况。如果有人在读 log 文件, 他们通常会希望读到系统出现的任何警告。 INFO : 用于基本的、高层次的诊断信息。在长时间运行的代码段开始运行及结束运行时应该产生消息,以便知道现在系统在干什么。但是这样的信息不宜太过频繁。 DEBUG :用于协助低层次的调试。 TRACE :用于展现程序执行的轨迹。 Log4j2 类图
通过类图可用看到:
每一个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应该在何处被转发、何处被驳回、何处被执行。
Log4j 中各个概念的简要介绍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 为换行符。
Reference
《 log4j user guide 》
分类: WEB开发 , 工作日志 , 异常处理
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于博客园博问闪存首页新随笔联系管理订阅 随笔 153 文章 0 评论 41 Log4j架构及概念的详细内容...