log4j2中文手册

更新时间:2024-04-07 12:35:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

Log4j2使用手册

一.Log4j2介绍

Log4j1.x被广泛应用于应用程序,但是近年发展明显放缓,因为要维持较老java版本的使用,使得log4j1.x的发展更困难。而作为其代替品, slf4j/logback做出了许多必要改进,为什么还需要log4j2? 主要有以下几个原因 : (1) Log4j2被设计用作审计日志框架, log4j和logback在重载配置时,都会丢失日志时间,而log4j2不会。Logback

中appenders中的异常对于应用来说是不可见的,log4j2可以配置异常向应用渗透。

(2) Log4j2 包含基于 LMAX Disruptor library的下一代无锁 Asynchronous Loggers,在多线程环境下,

Asynchronous Loggers相比slf4j/ logback提高了10倍以上的吞吐量,并且有着更低的延时。

(3) Log4j2的插件机制,使得在不需要修改框架的情况下,通过添加 Appenders, Filters, Layouts, Lookups轻松

扩展框架。

(4) 简单的插件配置,无需指定具体类名即可在configuration中配置插件。 (5) 支持自定义日志级别,可以在代码或者 configuration中自定义日志级别。

(6) 支持lambda表达式,java8的应用可以在请求日志级别启用时使用 lambda表达式懒构建一个日志消息,不

需要显示的日志级别检查,使得代码更简洁。

(7) 支持消息对象,消息允许支持有趣和复杂的结构,传递到日志记录系统,并且可以高效的操作。用户可以自由

创建消息类型和编写Layouts, Filters and Lookups来操作这些消息。

(8) Log4j1在Appenders 上支持Filters 。logback增加了TurboFilters ,允许在日志事件在处理前进行过滤。Log4j2

可以配置Filters在Logger后者Appender前运行。

(9) 许多Logback的Appenders不接受Layout,并且只能按照固定格式发送日志数据。大部分 log4j2接收Layout

配置,允许日志数据按照任何所需格式传输。

(10) Log4j1与logback的Layouts是返回一个String类型,这可能导致一些编码问题。Log4j2使用了简单的方式:

返回一个 byte数组。这以为着Layouts可以用在几乎任何appenders,而不仅仅是写入OutputStream。 (11) Syslogappender支持 TCP与UDP,以及BSDsyslog和 RFC5424格式。

(12) Log4j2得益于java5的并发支持,将锁的可能降到最低水平。Log4j1有已知的死锁问题,Logback也需要使

用synchronization来保持在相当高的锁级别。 (13) Apache开源

二.Log4j2架构

1. Log4j2类图

应用程序调用Log4j API时需要向LogManager传入一个特定的名称来获取一个Logger实例。LogManager会定位一个合适的 LoggerContext然后从中获取Logger。如果 Logger必须新建,那么与之关联的 LoggerConfig 遵

守如下规则 : (1)与Logger名称完全相同。(2)父Logger的名称。(3)Root LoggerConfig。LoggerConfig 对象是根据configuration 配置中的Logger声明创建的。LoggerConfig又与处理LogEvents的LoggerConfig关联。

2. 日志层次

logging API相比于 纯粹的System.out.println 最重要的不同是 :logging API 可以禁用一些log语句输出的同时允许其他一些语句块输出。这种能力建立在开发者按照一定规则将日志分类的基础上。

Log4j1.x的层次关系是通过 Loggers之间的关系保持的。而 Log4j2.x是通过 LoggerConfig 对象来维持这种层次关系的。

Loggers 与LoggerConfigs 都是带名称的实体。Logger名称是大小写敏感的,并且符合如下命名层级规则: 一个LoggerConfig 的名字是另外一个LoggerConfig名字加上 . 和一些后缀字符。那么这个 LoggerConfig是另外的LoggerConfig的子类。类似于 java的 package路径。例如 : 一个名称为\的LoggerConfig 是一个名称为\的LoggerConfig 的父类。类似的,“java” 是\的父类和

\的祖先类。开发人员对这样的命名方案应该很熟悉。

RootLoggerConfig居于LoggerConfig分层的顶层。RootLoggerConfig总是存在于每个LoggerConfig的层次中。任何一个与root LoggerConfig相关联的Logger可以通过如下方式获得:

Logger logger =LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); 也可以用如下简便方式:

Logger logger =LogManager.getRootLogger(); 其他的 Logger可以使用 LogManager.getLogger 的静态方法根据传入的名称获得。

3. LoggerContext

LoggerContext作为日志系统的锚点(用于根据日志名称定位日志输出信息的Logger),在不同的环境中,一个应用系统中可能存在多个有效的 LoggerContexts ,

4. Configuration

每个 LoggerContext 有一个有效的Configuration 。Configuration 包括所有的Appenders,上下文过滤器,LoggerConfigs 与包含StrSubstitutor的引用信息。在配置重载期间,两个Configuration 共存,一旦所有的 Logger重定向到新的Configuration ,旧的Configuration 将停用和丢弃。

5. Logger

Loggers 是调用LogManager.getLogger静态方法创建的。Logger本身不直接执行动作。它只有一个名称,并于一个LoggerConfig相关联。它继承自 AbstractLogger,当 configuration 被修改时,Loggers 将关联修改后的LoggerConfig,从而改变这个Loggers 的行为。

获取Loggers:

使用相同的名称调用 LogManager.getLogger方法,总是返回完全相同的Logger 对象。 例如:

Logger x =LogManager.getLogger(\); Logger y =LogManager.getLogger(\); X和y指向完全相同的 Logger对象。

Log4j配置环境通常是在应用程序初始化时完成的。最好的方式是读取配置文件。

Log4j很容易通过类名来命名,这可以在每个类初始化的时候完成Logger 的初始化,Logger 的名称就等于类的完整路径,这是一个定义Logger的简单有效的方式。当为日志文件输出具有 Logger名称的日志时,这种命名策略可以很容易看出一个日志消息的来源。当然这只是一种比较常见的日志命名方式,log4j并没有对此进行限制,开发人员可以按照需求进行命名。

因为使用类名命名 Logger是一个习惯用法。一个便利的方法是使用LogManager.getLogger() 来获取类的全路径名称的 Logger。

目前为止,使用类名来作为Logger 的名称是最好的方式。

6. LoggerConfig

当Logger对象在logging configuration 中被定义时, LoggerConfig对象同时被创建。LoggerConfig 包含一组Filters 来过滤传递到Appenders 的LogEvent 。LoggerConfig使用一组Appenders 用来处理这些事件。

Log Levels

LoggerConfigs 被分配一个日志级别,包括: TRACE, DEBUG, INFO, WARN, ERROR, FATAL。Log4j 2 也支持自定义日志级别,另一种获得更多日志粒度的方式是使用 Markers。

Log4j1 与logback都有日志级别继承的概念。在Log4j2中,因为 Loggers 和LoggerConfigs是2个不同的对象,所以这个概念的实现有所不同。但是因为每个Loggers都关联一个相应的LoggerConfig,所以最终效果是相同的。

如果一个 root LoggerConfig没有配置日志级别,将分配一个默认级别。 Logger Name root X X.Y X.Y.Z Assigned LoggerConfig root root root root LoggerConfig Level DEBUG DEBUG DEBUG Logger Level DEBUG DEBUG DEBUG Example 1 在 Example1中, 仅配置了root logger与其日志级别,所有的Loggers都会引用 root LoggerConfig ,并使用其日志级别。 Logger Name root X X.Y X.Y.Z Assigned LoggerConfig root X X.Y X.Y.Z LoggerConfig Level DEBUG ERROR INFO WARN Example 2 在Example2中, 每个Logger 都有其对应的LoggerConfig 和相应的日志级别。

Level DEBUG ERROR INFO WARN Logger Name root X X.Y X.Y.Z Assigned LoggerConfig root X X X.Y.Z LoggerConfig Level DEBUG ERROR ERROR WARN Level DEBUG ERROR ERROR WARN Example 3 在 Example3中,Loggerroot, X 和X.Y.Z 都有与名称完全匹配的LoggerConfig 配置,而 LoggerX.Y没有与其命名匹配的 LoggerConfig ,所以使用拥有最长匹配度的 X的LoggerConfig 。 Logger Name root X X.Y X.Y.Z Assigned LoggerConfig root X X X LoggerConfig Level DEBUG ERROR ERROR ERROR Example 4 Example 4中,root 与X各自有一个与名称完全匹配的LoggerConfig ,而X.Y 与X.Y.Z没有与名称完全匹配的LoggerConfig ,所以使用与其名称有最长匹配度的X 的LoggerConfig 。 Logger Name root X Assigned LoggerConfig root X LoggerConfig Level DEBUG ERROR level level DEBUG ERROR ERROR ERROR DEBUG ERROR X.Y X.YZ X.Y X INFO ERROR Example 5 INFO ERROR Example 5中root.X, 与X.Y 有与名字完全匹配的LoggerConfig ,而 X.YZ 将与X 相匹配而不是X.Y,因为路径匹配时按照 .进行分割和匹配的。 Logger Name root X X.Y X.Y.Z Assigned LoggerConfig root X X.Y X.Y LoggerConfig Level DEBUG ERROR Example 6 Example 6中,X.Y 与X.Y.Z都没有配置相应的日志级别,所以他们从 Logger X中继承了日志级别。

下面的表格展示了logEvent(垂直方向)与 LoggerConfig(水平方向)的过滤方式:

Level DEBUG ERROR ERROR ERROR Event Level ALL TRACE DEBUG INFO WARN ERROR FATAL OFF 7. Filter

TRACE YES YES YES YES YES YES YES NO DEBUG YES NO YES YES YES YES YES NO LoggerConfig Level INFO YES NO NO YES YES YES YES NO WARN YES NO NO NO YES YES YES NO ERROR YES NO NO NO NO YES YES NO FATAL YES NO NO NO NO NO YES NO OFF NO NO NO NO NO NO NO NO Log4j2提供了 Filters, 可以 应用于 控制权传递到 LoggerConfig之前,控制权传递到LoggerConfig之后 但是在调用 Appenders之前,控制权在传递到 LoggerConfig之后,但是在调用一个指定的 Appender 之前。类似于防火墙的处理方式,每个Filters可以返回三个结果的其中之一。 Accept, Deny Neutral 。 Accept意味着不会再调用其他Filters 了,LogEvent 将被执行。

Deny 意味着立即忽略这个LogEvent,并将这个LogEvent 的控制权交还给它调用者。

Neutral 指LogEvent将传递给其他Filters ,如果没有别的Filters 了,那么这个LogEvent将被执行。

尽管一个LogEvent 可能被一个Filter接收,但是这个LogEvent仍然没有被日志记录下来,发生这种情况的场景可能是LogEvent被pre-LoggerConfig Filter接收了,但是却被LoggerConfig拒绝了,或者被所有的Appenders拒绝

了。

8. Appender

由logger的不同来决定一个logging request是被禁用还是启用只是log4j2的情景之一。log4j2还允许将logging request中log信息打印到不同的目的地中。在log4j2的世界里,不同的输出位置被称为Appender。目前,Appender可以是console、文件、远程socket服务器、Apache Flume、JMS以及远程 UNIX 系统日志守护进程,数据库。一个Logger可以绑定多个不同的Appender。

可以通过调用当前Configuration的addLoggerAppender 方法为Logger增加一个Appender 。如果不存在一个与Logger名称相对应的LoggerConfig,那么相应的LoggerConfig将被创建,并且新增加的Appender将被添加到此新建的LoggerConfig中。然后,所有的Loggers将会被通知更新自己的LoggerConfig引用(PS:一个Logger的LoggerConfig引用是根据名称的匹配长度来决定的,当新的LoggerConfig被创建后,会引发一轮配对洗牌)。

某个logger 中被启用的 logging request 将被转发到该LoggerLoggerConfig 相关联的的所有Appenders上,并且还会被转发到LoggerConfig的父级的Appenders上。

换句话说,Appenders将从LoggerConfig的层次结构中获得继承。

例如,对LoggerConfig B来说,它的父级为A,A的父级为root。如果在root中定义了一个Appender为console,那么所有启用了的logging request都会在console中打印出来。另外,如果LoggerConfig A定义了一个文件作为Appender,那么使用LoggerConfig A和LoggerConfig B的logger 的logging request都会在该文件中打印,并且同时在console中打印。

如果想避免这种遗传效应的话,可以在configuration文件中做如下设置:additivity=\

这样,就可以关闭Appender的遗传效应了。

具体解释如下 :

Appender Additivity The 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 \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.

上面例子的详细表格解释如下: Logger Name root Added Appenders A1 Additivity Flag not applicable true Output Targets A1 Comment The root logger has no parent so additivity does not apply to it. Appenders of \x A-x1, A-x2 A1, A-x1, A-x2 A1, A-x1, A-x2 x.y none true Appenders of \typical to configure a Logger with no Appenders. Appenders in \x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1 A-sec security A-sec false No appender accumulation since the additivity flag is set to false. security.access none true A-sec Only appenders of \additivity flag in \false.

9. Layout

通常,用户不止希望能定义log输出的位置,还希望可以定义输出的格式。这就可以通过将Appender与一个layout相关联来实现。Log4j中的 一个标准定义PatternLayout,就允许用户使用一种类似C语言printf函数的打印格式, 如\格式在真实环境下会打印类似如下的信息: 176 [main] INFO org.foo.Bar - Located nearest gas station.

第一个字段是启动的毫秒数,第二个字段是日志请求的线程号, 第三个字段是日志请求级别,第四个是与日志请求相关联的日志名称, 在“-” 之后的内容就是日志内容。

Log4j有各种不同的 Layouts, 例如 : JSON, XML, HTML, 和Syslog (including the new RFC 5424 version). 其他一些appenders例如database connectors会使用指定的字段替代特定的文本布局。

同样重要的是,要使得log4j的日志消息内容符合用户指定的标准。,例如:在你的工程中,你需要一个Oranges类型的日志对象,你就可以创建一个OrangeMessage,可以接受Orange实例并且传递到log4j,Orange对象就会被格式化为一个适当的字节数组。

10. StrSubstitutor and StrLookup

StrSubstitutor 类与 StrLookup接口是来自Apache Commons Lang, 并且被修改为支持 LogEvents 的运算。

此外 来自Apache Commons Configuration的 Interpolator类允许StrSubstitutor 运算来着多个StrLookups 的变量,它也被修改支持LogEvents的运算。

Log4j2提供了一种机制,可以使得configuration引用 来自System Properties, the configuration file, the ThreadContext Map, StructuredData中的变量。

当 configuration被加载或者 每个LogEvent 运行时,这个变量将被解析。如果这个日志组件可以处理这个变量。

三.Log4j2 API

Log4j2API提供了应用程序使用的接口,也提供了创建一个日志实现所需的适配器组件, Log4j2解耦了API及其实现之间的关系。这样做的目的是不允许有多个实现 。

一种可能的情况是 一个Log4j2的API对应多个实现,所以在应用中要使用 Log4j2 API中的类和方法, 而不要使用具体的实现内容

(1)Hello World!

下面是一个helloworld的例子, 有一个从LogManager中获取的 名称为 HelloWorld的Logger,接着,这个Logger打印了 \的日志消息。 当然,只有在configured 中做了相应的配置,这些内容才能打印出来。

1. import org.apache.logging.log4j.LogManager; 2. import org.apache.logging.log4j.Logger; 3.

4. publicclassHelloWorld{

5. privatestaticfinalLogger logger =LogManager.getLogger(\); 6. publicstaticvoid main(String[] args){ 7. logger.info(\); 8. }

9. }

调用方法 logger.info()的输出会有所不同,主要取决于 configuration的配置

(2) Substituting Parameters

日志记录的目的是要提供系统中发生的信息,这就要打印一些对象信息,在 Log4j1.x中, 可以通过这样来打印 :

1. if(logger.isDebugEnabled()){

2. logger.debug(\+ user.getName()+\

\+user.getBirthdayCalendar()); 3. }

这样的代码感觉很繁琐,而且会检查2次日志级别,一次是 logger.isDebugEnabled,另一次是logger.debug。

在log4j2中更好的实现方式是 :

logger.debug(\in user {} with birthday {}\, user.getName(), user.getBirthdayCalendar()); 代码简洁了很多, 而且日志只有在debug 打印的时候才会构建日志内容中的字符串。

(3) Formatting Parameters

类似于java的Formatter, Formatter Loggers 可以提供简易的字符串格式化方式 : 例如:

1. publicstaticLogger logger =LogManager.getFormatterLogger(\); 2.

3. logger.debug(\, user.getName(),

user.getBirthdayCalendar());

4. logger.debug(\, user.getName(),

user.getBirthdayCalendar());

5. logger.debug(\,Integer.MAX_VALUE); 6. logger.debug(\,Long.MAX_VALUE);

要使用格式化日志,必须调用 LogManager getFormatterLogger的方法。 下面展示了例子中的结果内容 :

1. 2012-12-1211:56:19,633[main] DEBUG:UserJohnSmithwith birthday

java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id=\,offset=-18000000,dstSavings=3600000

,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=?,YEAR=1995,MONTH=4,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=23,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?]

2. 2012-12-1211:56:19,643[main] DEBUG:UserJohnSmithwith birthday 0523,1995 3. 2012-12-1211:56:19,643[main] DEBUG:Integer.MAX_VALUE =2,147,483,647

4. 2012-12-1211:56:19,643[main] DEBUG:Long.MAX_VALUE =9,223,372,036,854,775,807

(4) Mixing Loggers with Formatter Loggers

Formatter loggers是用来控制细粒度的输出格式的。但是缺点是必须指定正确的类型,例如给 %d 格式化参数传递了 Integer以外的类型,就会抛错。

如果你主要使用 {} 风格的参数, 但是偶尔要精确控制输出格式,可以使用 printf方法:

1. publicstaticLogger logger =LogManager.getLogger(\); 2.

3. logger.debug(\, someDataSource);

4. logger.printf(Level.INFO,\,

user.getName(), user.getBirthdayCalendar());

(5) Java 8 lambda support for lazy logging

在2.4版本以后,Logger 支持了lambda表达式,在请求日志级别启动的时候,可以懒加载的打印这些信息而不用去显式检查日志是否要打印。 例如, 在以前版本中可能这样写 :

1. // pre-Java 8 style optimization: explicitly check the log level

2. // to make sure the expensiveOperation() method is only called if necessary 3. if(logger.isTraceEnabled()){

4. logger.trace(\, expensiveOperation()); 5. }

在java8中可以使用 lambda表达式实现相同的效果,你可以不用显式的检查日志级别了:

1. // Java-8 style optimization: no need to explicitly check the log level: 2. // the lambda expression is not evaluated if the TRACE level is not enabled 3. logger.trace(\,()-> expensiveOperation());

(6)Logger Names

大部分日志实现使用为了使得 日志名称与日志配置匹配 使用一个分层的名称方案。在这个方案中Logger的名称是由 . 分隔的, 非常类似于 java

包名的层次。 例如 :org.apache.logging

是 org.apache.logging.appender 与 org.apache.logging.filter 的父层。大多数情况下,应用命名他们的日志名称是通过在LogManager.getLogger中传入当前类名实现的。因为这个用法太常见,Log4j2在入参为空或者传入为Null的时候默认是提供当前类的名称。例如,下面的例子的 Logger的名称都是 \

1. package org.apache.test; 2.

3. publicclassMyTest{

4. privatestaticfinalLogger logger =LogManager.getLogger(MyTest.class); 5. }

1. package org.apache.test; 2.

3. publicclassMyTest{

4. privatestaticfinalLogger logger =LogManager.getLogger(MyTest.class.getName()); 5. }

1. package org.apache.test; 2.

3. publicclassMyTest{

4. privatestaticfinalLogger logger =LogManager.getLogger(); 5. }

1. Flow Tracing

Logger 类提供了针对跟踪应用执行路径的很有用的方法 。这些方法可以从debug 日志中产生可以单独 过滤的logging events。以下情况鼓励使用这些方法: (1) 在不需要DEBUG的开发中帮助诊断问题 (2)在不需要DEBUG的生产中帮助诊断问题。 (3)帮助新的开发人员在应用中学习使用。

最常用的2个方法是 entry() 与exit() 。

entry() 一般置于方法的开头,entry() 可以传入0-4个参数,通常是传入方法变量, entry() 方法的日志级别是TRACE ,并且使用了一个名为ENTER 的Marker。这也是一个FLOW Marker

exit() 放置在方法的结束或者方法的返回前,exit() 可以传入 1个参数或者不传入参数,通常 void方法不传入参数,而返回一个Object的方法使用exit(Object obj) 。exit()方法使用TRACE 的日志级别,并且使用了一个名为EXIT 的Marker。

throwing() 方法一般在应用抛出一个不太可能处理的异常时使用。类似RuntimeException 。这个方法使用了ERROR 的日志级别。

catching() 方法使用在捕获一个异常,但是不会抛出的时候,这个方法使用了ERROR 的日志级别。,

下面的例子展示了这些方法的典型使用:

1. package com.test; 2.

3. import org.apache.logging.log4j.Logger; 4. import org.apache.logging.log4j.LogManager; 5.

6. import java.util.Random; 7.

8. publicclassTestService{

9. privateLogger logger =LogManager.getLogger(TestService.class.getName()); 10.

11. privateString[] messages =newString[]{ 12. \, 13. \, 14. \ 15. };

16. privateRandom rand =newRandom(1); 17.

18. publicString retrieveMessage(){ 19. logger.entry(); 20.

21. String testMsg = getMessage(getKey()); 22.

23. return logger.exit(testMsg); 24. } 25.

26. publicvoid exampleException(){ 27. logger.entry(); 28. try{

29. String msg = messages[messages.length];

30. logger.error(\); 31. }catch(Exception ex){ 32. logger.catching(ex); 33. }

34. logger.exit(); 35. } 36.

37. publicString getMessage(int key){ 38. logger.entry(key); 39.

40. String value = messages[key];

41.

42. return logger.exit(value); 43. } 44.

45. privateint getKey(){ 46. logger.entry();

47. int key = rand.nextInt(messages.length); 48. return logger.exit(key); 49. } 50. }

测试程序的调用 :

1. package com.test; 2.

3. publicclassApp{ 4.

5. publicstaticvoid main(String[] args ){ 6. TestService service =newTestService(); 7. service.retrieveMessage(); 8. service.retrieveMessage(); 9. service.exampleException(); 10. } 11. }

日志配置文件 :

1. 2. 3.

4.

5. 6.

8. 9.

10.

11. 12. 13. 14.

15. 16. 17.

18. 19.

2. Markers

日志记录框架的主要目的之一是在需要的时候提供一种方法来生成调试和诊断信息。并且可以对这些信息进行过滤,以使不会压垮系统或者需要使用它的人,例如:应用需要打印 入口,出口,和分别执行的SQL语句,并且希望能够分开 查询和 更新语句 :

1. import org.apache.logging.log4j.Logger; 2. import org.apache.logging.log4j.LogManager; 3. import java.util.Map; 4.

5. publicclassMyApp{ 6.

7. privateLogger logger =LogManager.getLogger(MyApp.class.getName()); 8. privatestaticfinalMarker SQL_MARKER =MarkerManager.getMarker(\); 9. privatestaticfinalMarker UPDATE_MARKER

=MarkerManager.getMarker(\).set(SQL_MARKER); 10. privatestaticfinalMarker QUERY_MARKER

=MarkerManager.getMarker(\).set(SQL_MARKER); 11.

12. publicString doQuery(String table){ 13. logger.entry(param); 14.

15. logger.debug(QUERY_MARKER,\, table); 16.

17. return logger.exit(); 18. } 19.

20. publicString doUpdate(String table,Mapparams){ 21. logger.entry(param); 22.

23. if(logger.isDebugEnabled()){

24. logger.debug(UPDATE_MARKER,\, table, formatCols()); 25.

26. return logger.exit(); 27. } 28.

29. privateString formatCols(Map cols){ 30. StringBuilder sb =newStringBuilder(); 31. boolean first =true;

32. for(Map.Entry entry : cols.entrySet()){ 33. if(!first){ 34. sb.append(\); 35. }

36. sb.append(entry.getKey()).append(\).append(entry.getValue()); 37. first =false; 38. }

39. return sb.toString(); 40. } 41. }

在上面的例子中,可以通过添加 MarkerFilters 来打印query的日志, update的日志,或者所有的日志。

在使用Markers有一些重要规则

(1) Markers必须是唯一的。他们是不可变的注册名称,要确保你的应用中的Markers都是不同的。

(2) 父 Markers可以被动态的添加或者移除,但是这个操作是很昂贵的,建议在第一次获得Markers时就确定父

Markers。

(3) 恰当的设置父类 Markers。

3. EventLogger

EventLogger类提供了一个在应用中产生 logging events的简单机制。EventLogger是初始化 日志审计系统中事件的一个很有用的方式。

一个推荐的使用方法是在 WEBapplication环境中, 可以使用request 中的内容填充ThreadContext Map,例如用户ID,用户的IP地址,产品名称等。这可以在一个servlet filter 中很简单的实现,并且在request 的结尾,ThreadContext Map 将被清除。

1. import org.apache.logging.log4j.ThreadContext; 2. import org.apache.commons.lang.time.DateUtils; 3.

4. import javax.servlet.Filter; 5. import javax.servlet.FilterConfig; 6. import javax.servlet.ServletException; 7. import javax.servlet.ServletRequest; 8. import javax.servlet.ServletResponse; 9. import javax.servlet.FilterChain; 10. import javax.servlet.http.HttpSession; 11. import javax.servlet.http.HttpServletRequest; 12. import javax.servlet.http.Cookie;

13. import javax.servlet.http.HttpServletResponse; 14. import java.io.IOException; 15. import java.util.TimeZone; 16.

17. publicclassRequestFilterimplementsFilter{ 18. privateFilterConfig filterConfig;

19. privatestaticString TZ_NAME =\; 20.

21. publicvoid init(FilterConfig filterConfig)throwsServletException{ 22. this.filterConfig = filterConfig; 23. } 24. 25. /**

26. * Sample filter that populates the MDC on every request. 27. */

28. publicvoid doFilter(ServletRequest servletRequest,ServletResponse

servletResponse,FilterChain filterChain) 29. throwsIOException,ServletException{

30. HttpServletRequest request =(HttpServletRequest)servletRequest; 31. HttpServletResponse response =(HttpServletResponse)servletResponse; 32. ThreadContext.put(\, request.getRemoteAddr()); 33. HttpSession session = request.getSession(false); 34. TimeZone timeZone =null; 35. if(session !=null){

36. // Something should set this after authentication completes 37. String loginId =(String)session.getAttribute(\); 38. if(loginId !=null){

39. ThreadContext.put(\, loginId); 40. }

41. // This assumes there is some javascript on the user's page to create the cookie. 42. if(session.getAttribute(TZ_NAME)==null){ 43. if(request.getCookies()!=null){

44. for(Cookie cookie : request.getCookies()){ 45. if(TZ_NAME.equals(cookie.getName())){

46. int tzOffsetMinutes =Integer.parseInt(cookie.getValue()); 47. timeZone =TimeZone.getTimeZone(\);

48. timeZone.setRawOffset((int)(tzOffsetMinutes *DateUtils.MILLIS_PER_MINUTE)); 49. request.getSession().setAttribute(TZ_NAME, tzOffsetMinutes); 50. cookie.setMaxAge(0); 51. response.addCookie(cookie); 52. } 53. } 54. } 55. } 56. }

57. ThreadContext.put(\, servletRequest.getServerName());

58. ThreadContext.put(\, filterConfig.getInitParameter(\)); 59. Threadcontext.put(\, servletRequest.getLocale().getDisplayName()); 60. if(timeZone ==null){

61. timeZone =TimeZone.getDefault(); 62. }

63. ThreadContext.put(\, timeZone.getDisplayName()); 64. filterChain.doFilter(servletRequest, servletResponse); 65. ThreadContext.clear(); 66. } 67.

68. publicvoid destroy(){ 69. } 70. }

当需要通过StructuredDataMessage 记录时间时,可以创建和为StructuredDataMessage 赋值,然后调用 EventLogger.logEvent(msg)

1. import org.apache.logging.log4j.StructuredDataMessage; 2. import org.apache.logging.log4j.EventLogger; 3.

4. import java.util.Date; 5. import java.util.UUID; 6.

7. publicclassMyApp{ 8.

9. publicString doFundsTransfer(Account toAccount,Account fromAccount,long amount){ 10. toAccount.deposit(amount); 11. fromAccount.withdraw(amount);

12. String confirm = UUID.randomUUID().toString();

13. StructuredDataMessage msg =newStructuredDataMessage(confirm,null,\); 14. msg.put(\, toAccount); 15. msg.put(\, fromAccount); 16. msg.put(\, amount); 17. EventLogger.logEvent(msg); 18. return confirm; 19. } 20. }

EventLogger 类使用一个名字为EventLogger 的Logger。EventLogger使用一个OFF的默认日志级别,它不可以被过滤,这个事件可以通过StructuredDataLayout.来设置格式。

4. Messages

一般情况下, Logger接收传入的参数打印日志。比如一个应用中有一个 Map,其值如下 : {\\,还有一个User对象,其中有getId 方法返回 \值。开发者可能打印这些信息如下 :

logger.info(\, map.get(\), user.getId()); 会打印出 \的日志。 另一种方式是使用一个 Message来打印这个日志 :

logger.info(newLoggedInMessage(map, user)); 这种方式获取日志内容委托给了LoggedInMessage 的getFormattedMessage 方法。虽然这种方式创建了一个新的对象,但是在LoggedInMessage格式化以前,并不会调用 map和user的任何方法。这在你的Object对象的toString方法不能产生你想要的日志信息时很有用。

另一个好处是 Message简化了输出日志的Layout。 在其他日志框架中,Layout必须通过各自的参数来循环,并根据遇到的对象来确定做什么。但是 Messages的 Layout可以选择使用Message的格式化代理 或者基于遇到的Message类型来格式化。

以前面的打印 SQL的例子来说,如果要使用Message的方式打印日志。 首先定义Message 。

1. publicclassSQLMessageimplementsMessage{ 2. publicenumSQLType{ 3. UPDATE, 4. QUERY 5. }; 6.

7. privatefinalSQLType type; 8. privatefinalString table;

9. privatefinalMap cols; 10.

11. publicSQLMessage(SQLType type,String table){ 12. this(type, table,null); 13. } 14.

15. publicSQLMessage(SQLType type,String table,Map cols){ 16. this.type = type; 17. this.table = table; 18. this.cols = cols; 19. } 20.

21. publicString getFormattedMessage(){ 22. switch(type){ 23. case UPDATE:

24. return createUpdateString(); 25. break; 26. case QUERY:

27. return createQueryString(); 28. break; 29. default; 30. } 31. } 32.

33. publicString getMessageFormat(){ 34. return type +\+ table; 35. } 36.

37. publicObject getParameters(){ 38. return cols; 39. } 40.

41. privateString createUpdateString(){ 42. } 43.

44. privateString createQueryString(){ 45. } 46.

47. privateString formatCols(Map cols){ 48. StringBuilder sb =newStringBuilder(); 49. boolean first =true;

50. for(Map.Entry entry : cols.entrySet()){ 51. if(!first){ 52. sb.append(\); 53. }

54. sb.append(entry.getKey()).append(\).append(entry.getValue()); 55. first =false; 56. }

57. return sb.toString(); 58. } 59. }

接着我们可以使用定义的 SQLMessage

1. import org.apache.logging.log4j.Logger; 2. import org.apache.logging.log4j.LogManager; 3. import java.util.Map; 4.

5. publicclassMyApp{ 6.

7. privateLogger logger =LogManager.getLogger(MyApp.class.getName()); 8. privatestaticfinalMarker SQL_MARKER =MarkerManager.getMarker(\);

9. privatestaticfinalMarker UPDATE_MARKER =MarkerManager.getMarker(\, SQL_MARKER); 10. privatestaticfinalMarker QUERY_MARKER =MarkerManager.getMarker(\, SQL_MARKER); 11.

12. publicString doQuery(String table){ 13. logger.entry(param); 14.

15. logger.debug(QUERY_MARKER,newSQLMessage(SQLMessage.SQLType.QUERY, table)); 16.

17. return logger.exit(); 18. } 19.

20. publicString doUpdate(String table,Mapparams){ 21. logger.entry(param); 22.

23. logger.debug(UPDATE_MARKER,newSQLMessage(SQLMessage.SQLType.UPDATE, table, parmas); 24.

25. return logger.exit(); 26. } 27. }

可以看到,这个例子中,原来业务逻辑中格式化 SQL列的操作被放在了 SQLMessage中,如果需要,还可以在Filters和 Layouts中队这个 SQLMessage进行特殊处理。

Log4j2提供了了一些方便的 Message实现:

? FormattedMessage

The message pattern passed to a FormattedMessage is first checked to see if it is a valid

java.text.MessageFormat pattern. If it is, a MessageFormatMessage is used to format it. If not it is next checked to see if it contains any tokens that are valid format specifiers for String.format(). If so, a

StringFormattedMessage is used to format it. Finally, if the pattern doesn't match either of those then a ParameterizedMessage is used to format it. ? LocalizedMessage

LocalizedMessage is provided primarily to provide compatibility with Log4j 1.x. Generally, the best approach to localization is to have the client UI render the events in the client's locale.

LocalizedMessage incorporates a ResourceBundle and allows the message pattern parameter to be the key to the message pattern in the bundle. If no bundle is specified, LocalizedMessage will attempt to locate a bundle with the name of the Logger used to log the event. The message retrieved from the bundle will be formatted using a FormattedMessage. ? LoggerNameAwareMessage

LoggerNameAwareMessage is an interface with a setLoggerName method. This method will be called during event construction so that the Message has the name of the Logger used to log the event when the message is being formatted. ? MapMessage

A MapMessage contains a Map of String keys and values. MapMessage implements FormattedMessage and accepts a format specifier of \formatted as \? MessageFormatMessage

MessageFormatMessage handles messages that use a conversion format. While this Message has more flexibility than ParameterizedMessage, it is also about two times slower. ? MultiformatMessage

A MultiformatMessage will have a getFormats method and a getFormattedMessage method that accepts and array of format Strings. The getFormats method may be called by a Layout to provide it information on what formatting options the Message supports. The Layout may then call getFormattedMessage with one or more for the formats. If the Message doesn't recognize the format name it will simply format the data using its default format. An example of this is the StructuredDataMessage which accepts a format String of \cause it to format the event data as XML instead of the RFC 5424 format. ? ObjectMessage

Formats an Object by calling its toString method. ? ParameterizedMessage

ParameterizedMessage handles messages that contain \the replacement parameters. ? SimpleMessage

SimpleMessage contains a String that requires no formatting. ? StringFormattedMessage

StringFormattedMessage handles messages that use a conversion format that is compliant with java.lang.String.format(). While this Message has more flexibility than ParameterizedMessage, it is also 5 to 10 times slower. ? StructuredDataMessage

StructuredDataMessage allows applications to add items to a Map as well as set the id to allow a message to be formatted as a Structured Data element in accordance withRFC 5424. ? ThreadDumpMessage

A ThreadDumpMessage, if logged, will generate stack traces for all threads. If running on Java 6+ the stack traces will include any locks that are held. ? TimestampMessage

A TimestampMessage will provide a getTimestamp method that is called during event construction. The timestamp in the Message will be used in lieu of the current timestamp.

5. Thread Context

Log4j引入了 MDC(Mapped Diagnostic Context) 与 NDC的概念, MDC&NDC 概念详见http://www.ibm.com/developerworks/cn/web/wa-lo-usertrack/index.html#userTrackSample

例如在WEB环境下,所有用户的日志都会无序的打印到 log文件中, 所以很难区分日志是哪个用户的, 可以通过 MDC功能在Filter中为每个登陆用户的线程设置绑定的用户, 这样可以在 打印日志的时候使用 %x{key} 把线程绑定的用户打印出来。

Log4j2 中也有 MDC&NDC的概念, 只是把这2个概念合并到了一个 Thread Context中。

Fish Tagging 大多数现实中的系统都是支持并发处理的。在大多数的多线程系统中,不同的线程处理不同的用户,Logging很适合跟踪和调试复杂的分布式应用, 一个常见的 区分不同客户端输出日志的方法是为每个客户端单独实例化一个Logger,这样增大了日志的增加和管理的开销。

更轻量化的方式是为从同一个客户端发起的日志请求增加一个唯一标识,就像标记了一条鱼,并一直追踪它的运动。 可以在一个事务或者一个请求中使用一个公共标识来跟踪log事件,我们成为 Fish Tagging。

Log4j2提供了2种方式的 Fish Tagging , Thread Context Map 与Thread Context Stack 。 Thread Context Map 类似于 Map,是key/value 对的方式,可以根据key来查找value值,The Thread Context Stack 是堆栈的方式,可以根据堆栈的顺序和元素值来查找。相对来说,key/value 的方式更加灵活,推荐使用。

例子1:Thread Context Stack

1. ThreadContext.push(UUID.randomUUID().toString());// Add the fishtag; 2.

3. logger.debug(\); 4. . 5. . 6. .

7. logger.debug(\); 8. . 9. .

10. ThreadContext.pop();

例子2: Thread Context Map

1. ThreadContext.put(\, UUID.randomUUID().toString());// Add the fishtag; 2. ThreadContext.put(\, request.getRemoteAddr()); 3. ThreadContext.put(\, session.getAttribute(\)); 4. ThreadContext.put(\, request.getServerName()); 5. .

6. logger.debug(\); 7. . 8. .

9. logger.debug(\); 10. . 11. .

12. ThreadContext.clear();

Thread Context Map 和Thread Context Stack默认使用ThreadLocal 来管理每个线程。 Thread Context Map 也可以使用InheritableThreadLocal 通过设置isThreadContextMapInheritable 属性为 \。当使用这种配置时,Map中的内容将被传递到它的子线程。

getContext() and cloneStack() 方法可以获得Map 和Stack 的拷贝副本。

PatternLayout提供了打印 Thread Context Map 和Thread Context Stack 内容的方法:

? ? ? Use %X by itself to include the full contents of the Map. Use %X{key} to include the specified key. Use %x to include the full contents of the Stack.

四. Configuration

本章只介绍常用和特殊的配置, 全部内容参考 :

http://logging.apache.org/log4j/2.x/manual/configuration.html

插入到应用中的日志代码,需要很合理的规划。据统计,应用中有大约 百分之四 的代码是专门用来记录日志的 。因此即便是一个较小规模的应用,其中也有很多的日志代码。因此, 不用手工修改的去管理这些日志代码成为一种必要。

Log4j2可以通过4种方式进行配置 :

(1) 通过 XML, JSON, YAML,或者 Properties的配置文件 。

(2)编程方式, 通过创建 一个ConfigurationFactory和 Configuration的实现。

(3)编程方式,通过调用 Configuration暴露的API, 为默认的 configuration增加组件。 (4)编程方式,通过调用 Logger的内部方法 。

本章主要介绍通过配置文件的方式配置Log4j2

1. 自动配置

Log4j2可以在初始化的时候自动完成配置,当log4j2启动时,会定位所有的 ConfigurationFactory的配置,并且按照优先级的高底加载它们。Log4j2包括四种 ConfigurationFactory的实现,JSON, YAML, properties,XML。 1. Log4j2将从system property(JVM启动参数,或者System.setProperty设置)读取 log4j.configurationFile属

性,如果设置了对应的文件,Log4j2的 ConfigurationFactory 将尝试根据文件扩展名来加载文件。 2. 如果

systemproperty 没有设置,propertiesConfigurationFactory 将在

classpath

中查

找 log4j2-test.properties文件。 3. 如果没有找到,YAMLConfigurationFactory 将在 classpath中查找log4j2-test.yaml或者

log4j2-test.yml文件。

4. 如果没有找到, JsonConfigurationFactory 将在classpath中查找log4j2-test.json 或者

log4j2-test.jsn

5. 如果没有找到XML ConfigurationFactory 将在 classpath中查找log4j2-test.xml

6. 如果 test文件找不到,则PropertiesConfigurationFactory 将在classpath中查找log4j2.properties文

7. 如果未找到,YAML ConfigurationFactory 将在classpath中查找log4j2.yaml或者log4j2.yml 8. 如果未找到,JSON ConfigurationFactory 将在 classpath中查找log4j2.json或者log4j2.jsn 9. 如果未找到,XML ConfigurationFactory 将在classpath中查找log4j2.xml

10. 如果都未找到,DefaultConfiguration 将被使用,默认将日志输出到 console控制台。

例子 : MyApp 使用log4j2 1. import com.foo.Bar; 2.

3. // Import log4j classes.

4. import org.apache.logging.log4j.Logger; 5. import org.apache.logging.log4j.LogManager; 6.

7. publicclassMyApp{ 8.

9. // Define a static logger variable so that it references the 10. // Logger instance named \

11. privatestaticfinalLogger logger =LogManager.getLogger(MyApp.class); 12.

13. publicstaticvoid main(finalString... args){ 14.

15. // Set up a simple configuration that logs on the console. 16.

17. logger.trace(\); 18. Bar bar =newBar(); 19. if(!bar.doIt()){

20. logger.error(\); 21. }

22. logger.trace(\); 23. } 24. }

MyApp 引入log4j2相关的类,在类中定义了一个名为MyApp 的静态 Logger, 另外一个类 Bar 内容如下 : 1. package com.foo;

2. import org.apache.logging.log4j.Logger; 3. import org.apache.logging.log4j.LogManager; 4.

5. publicclassBar{

6. staticfinalLogger logger =LogManager.getLogger(Bar.class.getName()); 7.

8. publicboolean doIt(){ 9. logger.entry();

10. logger.error(\); 11. return logger.exit(false); 12. } 13. }

如果没有加载任何配置文件,log4j将提供一个默认的configuration 。默认的configuration 是通过DefaultConfiguration 定义的,将设置如下 :

? Root Logger将使用 ConsoleAppender

? ConsoleAppender 将使用PatternLayout的格式是 : \? 日志级别是 Level.ERROR

所以上面例子打印如下 :

17:13:01.540 [main] ERROR com.foo.Bar - Did it again! 17:13:01.540 [main] ERROR MyApp - Didn't do it.

默认配置相当于如下的 XML配置 :

1. 2. 3.

4.

5. 6. 7. 8.

9.

10. 11. 12. 13.

2. Additivity

假设只想将 com.foo.Bar的日志级别设置为 TRACE, 仅仅改变日志级别就不能达到这样的要求 ,解决方案是新增一个 Logger。

1. 2.

3. 4.

使用这个配置,com.foo.Bar 的所有日志将被记录,而其他模块只有 Error日志会被记录。

在这个配置中 ,com.foo.Bar 仍然将日志打印到 Console中,因为 com.foo.Bar并没有配置任何appenders ,所以就会使用 Root Logger的配置。事实上,如下配置

1. 2. 3.

4.

5. 6. 7. 8.

9. 10. 11.

12.

13. 14. 15. 16.

将打印 :

17:13:01.540 [main] TRACE com.foo.Bar - entry 17:13:01.540 [main] TRACE com.foo.Bar - entry

17:13:01.540 [main] ERROR com.foo.Bar - Did it again! 17:13:01.540 [main] ERROR com.foo.Bar - Did it again! 17:13:01.540 [main] TRACE com.foo.Bar - exit (false) 17:13:01.540 [main] TRACE com.foo.Bar - exit (false) 17:13:01.540 [main] ERROR MyApp - Didn't do it.

可以看到 com.foo.Bar 中的TRACE 被打印了2次,这是因为com.foo.Bar的appender 先被第一次使用,在

console中打印内容,然后com.foo.Bar的父Logger,即 Roor Logger被引用, logevent会传递到 Root Logger,然后在Console中再次打印。这就是所谓的additivity。在很多时候这个功能是需要禁用的,如下 : 1.

2. 3.

4.

5. 6. 7. 8.

9. 10. 11.

12.

13. 14. 15. 16.

在设置 additivity=\后, 日志事件不会再传递到父Logger。

3. 自动重载

当Log4j2的配置是从文件中加载时, log4j2可以监测文件的变化,并且自动重载这些配置,当配置

monitorInterval 属性,并且值不为0的时候,Log4j2就会在下次时间间隔重载配置。下面的配置 就会每30

秒检查一次文件的变化,如果发生变化,配置文件就会被重载, 最小时间间隔为 5秒 :

1. 2. 3. ...

4.

4. 使用 Chainsaw 智能处理日志文件

Chainsaw是一个Log4J包的 GUI日志查看器和过滤器。它使用SocketAppender听从LoggingEvent对象发送然后在一个图表中把它们显示出来。在优先级,线程名,类别名或者报文的基础上,这些事件都可以被过虑。它也可以写入事件记载到一个文件上。

Log4j2为 所有 file-based appenders 和 socket-based appenders 提供了“广播”功能。例如:对于 file-based appenders,广播中就包括日志文件的位置和格式,Chainsaw 或者其他扩展系统可以发现这些广播,并且使用广播中的信息对日志文件进行智能处理。

广播的曝光机制,以及广播的形式,是通过Advertiser来实现的。一个外部如果想要处理一个特定的Advertiser实现,就必须了解广播的配置与格式。例如,一个数据库的Advertiser 可能会将log详细配置存储在数据库的表中,然后扩展系统通过读取数据库的表去发现日志的位置与格式。

Log4j2提供了一个 Advertiser 的实现,“multicastdns,”是通过使用 http://jmdns.sourceforge.net的IP组播 来实现的 。

Chainsaw 会自动发现 log4j multicastdns 产生的广播,然后在Chainsaw's的Zeroconf tab中展示,然后开始跟踪和分析广播中的日志文件,目前,Chainsaw 只支持FileAppender 的广播。

广播在appender 中的配置:

1.在应用的classpath上增加 JmDns library ,http://jmdns.sourceforge.net 2.将configuration 元素的advertiser 属性设置为multicastdns 。 3.将appender 元素上的advertise 属性设置为 true

4.如果是 FileAppender-based 类型的配置,在appender 中将advertiseURI设置为合适的URI。

FileAppender-based 类型的配置要求在appender 上指定advertiseURI 属性,advertiseURI属性提供了Chainsaw如何访问该文件信息。例如:Chainsaw 可以通过ssh/sftp(使用Commons VFS (http://commons.apache.org/proper/commons-vfs/) sftp:// URI )远程访问,或者使用http:// URI通过 web server 来访问该文件,或者使用 file:// URI 来让本地运行的Chainsaw 实例来访问该文件

下面是一个启用广播的例子,可以通过 本地运行的 Chainsaw自动访问 。

Please note, you must add the JmDns library from http://jmdns.sourceforge.net to your application classpath in order to advertise with the 'multicastdns' advertiser

1. 2. 3. ...

4. 5.

6.

ut.log\advertise=\> 7. ... 8. 9.

5. Configuration 语法

本节只介绍 XML的语法,不在介绍 JSON, Properties, YAML的语法,需要的可以参考: http://logging.apache.org/log4j/2.x/manual/configuration.html#Loggers

在log4jxml配置中, configuration 元素属性如下 :

Attribute Name advertiser Description (Optional) The Advertiser plugin name which will be used to advertise individual FileAppender or SocketAppender configurations. The only Advertiser plugin provided is 'multicastdns\Either \The minimum amount of time, in seconds, that must elapse before the file configuration is checked for changes. The name of the configuration. A comma separated list of package names to search for plugins. Plugins are only loaded once per classloader so changing this value may not have any effect upon reconfiguration. Identifies the location for the classloader to located the XML Schema to use to validate the configuration. Only valid when strict is set to true. If not set no schema validation will take place. Specifies whether or not Log4j should automatically shutdown when the JVM shuts down. The shutdown hook is enabled by default but may be disabled by setting this attribute to \The level of internal Log4j events that should be logged to the console. Valid values for this attribute are \initialization, rollover and other internal actions to the status logger. Setting status=\ is one of the first tools available to you if you need to troubleshoot log4j. dest monitorInterval name packages schema shutdownHook status strict verbose Enables the use of the strict XML format. Not supported in JSON configurations. Enables diagnostic information while loading plugins.

Log4j2可以使用2种风格的XML配置,简洁风格和严格风格 。简洁风格的配置很简单,因为元素的名称就代表了他们匹配的组件,但是缺点是不能用xmlschema进行验证。

元素和属性名称是不区分大小写的 。另外,属性既可以被指定为一个XML的属性,也可以被指定为一个元素的文本值。 例如 :

%m%n 是一样的。

简洁风格的配置 :

1. ; 2. 3.

4. value 5. 6. 7. 8. 9. 10. 11. 12. ...

13. 14.

15.

16. 17. 18. ...

19. 20. 21. 22. 23.

严格配置风格 :

除了简洁的配置方式,log4j2也可以使用可以用XMLschema校验的xml格式,这是通过使用对象类型来替换元素名称来实现的。

1.

2. 4.

5. target/test.log 6.

7. 8.

9.

10.

11. 12.

13. 14. 15. 16.

17.

18. 19.

20. 21. 22. 23.

24.

25.

26. %d %p %C{1.} [%t] %m%n 27. 28.

29. 30. 31. 32.

33.

34. 35. 36. 37.

38. 39. 40.

41. 42. 43. 44.

45. 46. 47. 48. 49.

50.

6. Logger配置

在使用Logger元素的时候,一个对应的 LoggerConfig 将被配置,logger 元素必须指定一个 name属性,通常还要指定一个level 属性和additivity 属性。Level属性的值可以是 TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF 中的一个。如果没有指定则默认为ERROR。Additivity 属性可以指定为 true或者false,默认为false。

一个LoggerConfig(包括root LoggerConfig) 可以从ThreadContextMap 中取得属性值作为其属性的配置。ThreadContextMap 中的属性也可以被Appenders, Filters, Layouts 引用。这些属性中包含的变量可以在

configuration配置文件加载的时候被解析或者动态的在 logevent打印的时候解析。

LoggerConfig 将被配置有一个或者多个AppenderRef 元素,如果是多个appenders ,当发生 loggingevent的时候,他们每个都将被调用。

每个configuration 必须包含一个rootlogger。如果没有配置,那么一个将使用一个日志级别为ERROR 的Console appender 。

Rootlogger与其他 logger的区别如下 : 1.rootlogger没有 name属性 2.rootlogger不支持additivity 属性。

7. Appenders 配置

一个appender 可以通过指定appender 插件名称的元素名称配置,也可以指定appender 元素的type属性的值为appender 插件名称来配置。另外每个appender必须有一个唯一的name属性,这个name将作为 logger引用 appender的标识。

大多数 appender 支持layout 配置。

8. Filters

Log4j2可以在4个地方使用filter

1. 与appenders,loggers,properties 元素在同一级别,这的过滤器可以在loggerevent传递到LoggerConfig

前接收或者拒绝这些事件。

2. 在 logger元素中,这的过滤器可以针对 loggers接收或者拒绝事件。 3. 在appender 元素中,这的过滤器可以防止或者导致事件被appender处理。

4. 在一个appender 的引用元素中,这个过滤器用来确定这个Logger的 loggerevent是否要路由到appender。

可以使用 filters 代表的CompositeFilter 来配置一个组合的filter ,可以在filters 中配置多个filter 元素。 1.

2. 3.

4. target/test.log 5.

6. 7.

8.

9.

10. 11.

12.

13. 14. 15.

16. 17. 18. 19.

20. 21.

22. %d %p %C{1.} [%t] %m%n 23. 24.

25. 26. 27. 28.

29.

30. 31.

32. 33. 34. 35. 36.

37. 38. ${sys:user.name} 39. 40.

41. 42. 43.

44. 45. 46.

47. 48. 49. 50. 51.

52.

9. 属性替换

Log4j2支持在 configuration 中定义标记,这些标记可以引用定义在其他地方的属性。

有些属性在configuration 被加载的时候解析。有些属性会被传递给组件,在运行时被解析。Log4j2使用了 Apache Commons Lang's StrSubstitutor and StrLookup classes 去实现这个能力。类似于Ant或者maven,允许在configuration中引用声明为${name}的变量。

在下面的例子中, rolling file appender 的filename 即被声明为一个变量

1.

2. 3.

4. target/rolling1/rollingtest-$${sd:type}.log 5.

6. 7.

8.

9.

10. 11. 12.

13. 14.

15. 16. 17.

18. 20.

21. %d %p %c{1.} [%t] %m%n 22.

23. 24. 25.

26. 27. 28. 29. 30. 31.

32.

33. 34. 35. 36.

37. 38. 39. 40.

41.

42.

除了在配置文件中定义, 还有很多其他的属性来源,为了适应这种能力,log4j支持 ${prefix:name} 这样的语法。前缀标识代表这个属性来源于哪个 特定的上下文。这将在下节 Lookups详细说明。

Log4j支持如下的 上下文前缀: Prefix bundle Context Resource bundle. The format is ${bundle:BundleName:BundleKey}. The bundle name follows package naming conventions, for example:${bundle:com.domain.Messages:MyKey}. ctx date env jvmrunargs Thread Context Map (MDC) Inserts the current date and/or time using the specified format System environment variables A JVM input argument accessed through JMX, but not a main argument; see RuntimeMXBean.getInputArguments(). Not available on Android. Log4j configuration properties. The log4j expressions ${log4j:configLocation} and ${log4j:configParentLocation} respectively provide the absolute path to the log4j configuration file and its parent folder. main map sd A value set with MapLookup.setMainArguments(String[]) A value from a MapMessage A value from a StructuredDataMessage. The key \without the enterprise number. The key \will return the message type. Other keys will retrieve individual elements from the Map. sys

System properties 可以在 configuration中声明默认的属性映射,如果在指定的 lookup中找不到对应的值,那么就会使用默认的属性映射,

10.

Lookup 变量

StrLookup 的一个有趣的特性是,如果一个变量被声明为有多个 $ 前缀,每次该变量被解析的时候,头部的第一个$ 会被简单移除。在前面的例子中 \元素可以在运行时动态解析变量,为了实现此功能,变量的前缀需要指定2个$ 字符,当configuration 文件被加载时,第一个$ 变量被移除,因此当 Routes元素在运行解析的时候,使用的是\的变量声明,sd的意思是这个变量会从StructuredDataMessage 事件中获取,其中的 type属性会作为routing key 。不是所有的元素都支持运行时动态解析变量。

如果在 Lookup 中没有根据 key找到相关的 value,那么将使用configuration 文件中的properties 声明的值。如果还是没有找到 key对应的value,那么这个变量声明就被当作 value返回了。configuration 中的默认值的声明如下 :

1. 2. 3.

4. Audit 5. 6. ...

7.

最后,需要指出的是, RollingFileappender里面的变量声明在 configuration文件加载的时候 并不会被解析。这是因为 RollingFile 元素会延迟到 当一个匹配发生时才去解析 里面的变量。

意思就是 RoutingAppender里面的内容如果要使用 Lookup 变量, 只需要 使用一个 $前缀字符就可以了,因为 RoutingAppender 里面的内容在 configuration文件加载时并不会被解析。 而如果是在 RoutingAppender 元素外要使用 Lookup 变量, 则需要使用 两个 $ 前缀字符,例如 $${ctx:login}, 在 configuration文件解析的时候会移除第一个$, 那么剩下的变量就是 ${ctx:login}, 这个变量会在运行时从 ThreadContext中获取 login这个key对应的 value值,来作为 日志中 ${ctx:login}变量的值。

11. Scripts

Log4j可以在它的部分组件中使用JSR 223 脚本语言。所有支持JSR 223脚本引擎的语言都可以使用。例如 JavaScript, Groovy 或者Beanshell, log4j2直接支持JSR 223 脚本框架,只需要安装相应的语言jar即可。

具体不在介绍,详情参考http://logging.apache.org/log4j/2.x/manual/configuration.html#Scripts 可以在 log4j2的xml中编写脚本语言, 比如用来根据日志事件参数来选择打印日志的格式。

12. XInclude

Log4j2的XML配置文件 可以使用 XInclude来引用其他的XML文件

1.

2.

3. status=\name=\> 4.

5. xinclude-demo.log 6.

7.

8. 9. 10.

13. Status Messages

在 configuration 配置文件加载前,status logger level 日志级别可以通过 Systemproperty的

org.apache.logging.log4j.simplelog.StatusLogger.level控制

在 configuration 配置文件加载后, status logger level 可以通过 configuration 文件中的status 属性控制

其他不在介绍 ,详细可以查看

http://logging.apache.org/log4j/2.x/manual/configuration.html#StatusMessages

14. Testing in Maven

可以使用maven对log4j2进行打包前测试。

http://logging.apache.org/log4j/2.x/manual/configuration.html#UnitTestingInMaven

本文来源:https://www.bwwdw.com/article/uc8r.html

Top