本文主要是介绍【Java】log4j2 使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
现在的应用已经越来越离不开日志的支持,此前log4j已经提供了良好的日志实现,但是随着时间的推移,log4j已经在2015年停止了维护,取而代之的是log4j2。下面介绍下log4j2。
优点
这里只列举一些:
1.支持异步logger,效率可以提升10倍;
2.gc压力几乎为0;
3.插件式实现,易于扩展;
4.支持动态修改配置,不丢事件;
配置
位置:默认找classpath下log4j2.xml、log4j2.yaml、log4j2.yml或者log4j2.properties这些文件。如果没有找到认为没有配置文件。默认会只输出error日志到console。
ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console)
18:36:14.849 [main] ERROR com.liyao.Test - hello
上面这个默认的配置相当于以下显式的配置:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="error"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
下面是一个比较完整的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" name="XMLConfigTest" packages="org.apache.logging.log4j.test"><Properties><Property name="filename">target/test.log</Property></Properties><ThresholdFilter level="trace"/><Appenders><Console name="STDOUT"><PatternLayout pattern="%m MDC%X%n"/></Console><Console name="FLOW"><!-- this pattern outputs class name and line number --><PatternLayout pattern="%C{1}.%M %m %ex%n"/><filters><MarkerFilter marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/><MarkerFilter marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/></filters></Console><File name="File" fileName="${filename}"><PatternLayout><pattern>%d %p %C{1.} [%t] %m%n</pattern></PatternLayout></File></Appenders><Loggers><Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false"><ThreadContextMapFilter><KeyValuePair key="test" value="123"/></ThreadContextMapFilter><AppenderRef ref="STDOUT"/></Logger><Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false"><Property name="user">${sys:user.name}</Property><AppenderRef ref="File"><ThreadContextMapFilter><KeyValuePair key="test" value="123"/></ThreadContextMapFilter></AppenderRef><AppenderRef ref="STDOUT" level="error"/></Logger><Root level="trace"><AppenderRef ref="STDOUT"/></Root></Loggers></Configuration>
configuration配置
常用属性:
monitorInterval:单位是s,log4j2支持动态检测配置文件的变化,这个参数用于标识检测的周期;
name:配置文件的名字;
packages:用于指定搜索插件的包名路径;
status:是log4j2内部的日志的等级,比如trace,info等等;
logger配置
每一份配置都会包含多个logger,每一个logger又对应一个loggerConfig。logger和loggerconfig有命名层级体系。
Named Hierarchy
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.
logger(或者loggerconfig)命名是用点好分割的字符串,这个与包名一致。点号之前的部分是后面部分的祖先。比如com就是com.foo的祖先而com.foo就是com的孩子。logger和loggerconfig都遵循这个规则。
所有的logger都有一个根祖先,也就是root。
程序获取logger时,需要传入logger的名字,log4j2在做匹配时,实际是做一个最长匹配,意味着如果无法精确匹配,就最长匹配。另外,如果匹配到名字A,那么所有的A的祖先也会被匹配到(包括root)。当然这个传递性可以通过配置additivity属性来取消。
logger的命名可以不遵循包名,但是点号分割的继承关系是内在实现的。这个恰好和包名完美契合,所以用包名命名logger是一个很好的选择。
获取root:
Logger logger = LogManager.getRootLogger();
理解命名层级体系对logger检索很重要。
下面是几个例子,LoggerName是程序传入的名字,Assigned是目前已经配置的logger。
Lookup
很重要的功能,lookup为我们提供了在配置文件的宏替换的能力。这种替换可以是运行时替换也可以是非运行时替换。
非运行时替换发生在加载配置文件时,而运行时替换发生在程序执行时。宏定义一般是${var}形式。log4j2加载配置文件时,会使用apache common的一个类库做替换,规则就是匹配${}符号,如果是双$$符号,那么第一个$被认定为转义符,表示第二个$不替换,直接输出。所以$${var}不会被Apache common库替换,而是直接输出${var},这个变量会在实现运行时被替换。明白了替换规则和时机,如果我们想非运行时替换,只要一个$,如果是运行时替换最好两个$(防止在加载配置文件时就替换)。
下面看下可以替换的类型。
首先是最典型的加载配置文件替换,这种一般就是在配置文件里定义一个property,然后后面的配置直接使用这个property。
比如:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><properties><property name="prefix">haha</property></properties><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="${prefix}%d{HH:mm:ss.SSS}[%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
log.info("hello1");
将会输出:haha12:11:57.086[main] INFO com.liyao.App - hello1
context map替换,这是一种运行时替换。我们可在程序内使用log4j2提供的contextmap存储键值对,然后在配置文件中使用,配置文件中使用需要加上ctx前缀。比如:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="$${ctx:name} %d{HH:mm:ss.SSS}[%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
ThreadContext.put("name", "liyao6");
log.info("hello1");
最后输出:liyao6 12:25:47.047[main] INFO com.liyao.App - hello1
注意这里使用了双$符号。
java系统变量,也是一种运行时替换,需要使用java前缀。
例子:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="$${java:version} %d{HH:mm:ss.SSS}[%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
log.info("hello1");
最后输出:Java version 1.8.0_101 12:30:16.632[main] INFO com.liyao.App - hello1
date,也是运行时替换,区别在于仍然需要提供前缀date,但是不需要使用任何key,而是需要提供一个simpledateformat版本来格式化日期,比如:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="$${date:yyyy-MM-dd HH:mm:ss.SSS}[%t] %-5level %logger{36} - %msg%n"/></Console></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/></Root></Loggers>
</Configuration>
log.info("hello1");
最后将输出:2018-12-22 12:34:31.051[main] INFO com.liyao.App - hello1
这与之前%d格式指定日期效果相同,%d是另一种方式,会在之后的layout处介绍。用在layout格式处,二者等效。但是date lookup还可以用在其他地方,比如定义property时指定文件名等。
system属性,也是运行时绑定,需要sys前缀。比如:
<Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="$${sys:sys} %d{HH:mm:ss.SSS}[%t] %-5level %logger{36} - %msg%n"/></Console>
System.setProperty("sys", "p");
log.info("hello1");
最后将输出:p 12:41:48.053[main] INFO com.liyao.App - hello1
layout配置
这里重点记录下patternlayout的用法。patternlayout负责将一个日志事件格式化输出出来。一句日志可以由多个格式化符组成,每一个格式化符都可以输出一定的信息。
格式化符由3部分组成:
%;
可选的格式参数;
格式化标记;
其中%是必须的开始符号。
可选的格式参数表示输出的格式,其格式为:
“符号 数字 . 符号 数字”
点号左侧表示最少输出多少字符,如果不够用空格补充。如果没有符号或者是正号,表示默认右对齐。如果是负号,表示左对齐,一般会选择左对齐吧。
点号右侧表示最长输出多少字符,如果多了就截断。如果没有符号或者是正号,表示从开始截断,如果是负号,表示从末尾截断。
下面是一个打印类名的例子:
最后是格式化标记,表明要输出什么。比如日志内容就用m,输出日志的类名就用c等等。常用的有:
C或者class:类名
c或者logger:logger名字
L或者line:行号
m后者msg或者meesage:日志内容
M或者method:方法名
p或者level:日志级别
t或者thread:线程名
n:换行
d或者date:日期,日期的格式可以被指定,在d后面的{}内部指定。如果不指定,相当于%d{DEFAULT},其格式后面会贴。其实默认的格式已经可以满足很多场景了。
常见的格式如下:
最终在使用layout时,需要在appender标签下,加一个PetternLayout标签,其中的属性pattern用于指定日志格式化格式。
Appender配置
Appender表示日志目的地。log4j2支持的appender种类很多,这里只记录console和rollingfile两种。
console:顾名思义,打印至控制台。几个常用的属性:
name:appender的name;
target:只能填写SYSTEN_OUT或者SYSTEM_ERR。
rollingfileappender:
常用属性:
append:是否追加,默认为true。
name:appender的name;
fileName:日志文件的文件名,其实是全路径,如果没有,将会自动创建;
filePattern:这个属性非常非常重,字面上表示,当发生rolling时,该如何命名旧的日志文件名;但是更深层次地,它暗示了是否能进行基于date或者递增数字的rolling,和下面的TriggeringPolicy以及RolloverStrategy必须一一对应。如果想要用基于date的rolling模式,那么必须在pattern中指定date的模式,比如$${date:yyyy-MM-dd HH:mm:ss}.log这种(或者%d格式);如果想要用基于递增数字的rollling模式,那么必须在pattern中指定%i。如果在pattern中没有指定date模式,但是却在后面配置了基于date的triggering,那么会报错;反之,如果没有指定基于数字的模式,但在后面配置了基于文件大小的triggering,也不行。例子:
<RollingFile name="RollingFile" fileName="~/logs/app.log"filePattern="~/logs/app-%i.log"><PatternLayout><Pattern>%d %p %c{1.} [%t] %m%n</Pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy /><SizeBasedTriggeringPolicy size="1KB"/></Policies></RollingFile>
这里pattern里只有%i,最后配置了timebasedtrigger。报错:
2018-12-22 20:04:54,088 main ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.RollingFileAppender for element RollingFile: java.lang.IllegalStateException: Pattern does not contain a date java.lang.IllegalStateException: Pattern does not contain a date
所以,pattern一定要和后面的policy保持一致!!!!!
下面看具体配置:
TriggeringPolicy:配置触发一次rolling的时机,与pattern要一致;
RolloverStrategy:表明发生rolling时,如何重命名之前的文件;
下面看一下triggerpolicy:
Cron Triggering Policy:基于一个linux的cron表达式。其中使用属性schedule标签指定表达式,比如:
<CronTriggeringPolicy schedule="0 0 * * * ?"/>
TimeBased Triggering Policy:基于时间,即超过多少时间触发一次rolling,用interval指定时间,单位是小时,默认是1。
<TimeBasedTriggeringPolicy interval="6"/>
以上两个是基于date的rolling时机,必须在pattern中指定date模式;
SizeBased Triggering Policy:基于文件大小,使用后缀指明大小,比如MB或者KB。例子:
SizeBasedTriggeringPolicy size="250 MB"/>
Composite Triggering Policy:组合多种policy,定义时只需要提供一个<Policies>标签包含其他的policy即可。比如:
<Policies><OnStartupTriggeringPolicy /><SizeBasedTriggeringPolicy size="20 MB" /><TimeBasedTriggeringPolicy />
</Policies>
rollingpolicy:
rollingpolicy一般可以不指定,因为log4j2会为我们默认提供一个default的rollingpolicy。这个default支持接收一个date模式的参数或者一个自增的数值模式来rolling。具体如下:
默认的rollover strategy接受一个日期/时间模式和一个整数,其中这个整数,是RollingFileAppender本身指定的filePattern属性。如果date/time模式存在的话,它将会替换当前日期和时间的值。如果这个模式包含整数的话,它将会在每次发生rollover时,进行递增。如果模式同时包含date/time和整数,那么在模式中,整数会递增直到结果中的data/time模式发生改变。如果文件模式是以".gz", ".zip", ".bz2", ".deflate", ".pack200", or ".xz"结尾的,将会与后缀相匹配的压缩方案进行压缩文件。
通常这个默认的rolling(不配置)已经满足了大多数常规需求。
当然如果我们想配置,default的rollling支持下面的参数:
fileIndex:如果是“max”,表示高的比低的更新;反之,“min”表示低的比高的更新;
min:计数器的最小值,默认为1;
max:计数器的最大值,当达到最大值以后,旧的将会被删除;
另外还有一个DirectWrite Rollover Strategy,这里就不介绍了。
一些例子:
①下面是使用RollingFileAppender
的一个示例配置,并且是基于时间和大小的触发策略。其将会根据当前年和月份在未来7天内创建7个压缩包,并且使用gzip
进行压缩。
<RollingFile name="RollingFile" fileName="logs/app.log"filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"><PatternLayout><Pattern>%d %p %c{1.} [%t] %m%n</Pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy /><SizeBasedTriggeringPolicy size="250 MB"/></Policies></RollingFile>
②第二个例子显示一个rollover
策略,最多保留20个文件:
<RollingFile name="RollingFile" fileName="logs/app.log"filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"><PatternLayout><Pattern>%d %p %c{1.} [%t] %m%n</Pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy /><SizeBasedTriggeringPolicy size="250 MB"/></Policies><DefaultRolloverStrategy max="20"/></RollingFile>
③下面是使用RollingFileAppender
的一个示例配置,并且是基于时间和大小的触发策略。其将会根据当前年和月份在未来7天内创建7个压缩包,并且每6小时即被6整除时,会使用gzip
进行压缩每个文件。
<Appenders><RollingFile name="RollingFile" fileName="logs/app.log"filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log.gz"><PatternLayout><Pattern>%d %p %c{1.} [%t] %m%n</Pattern></PatternLayout><Policies><!--这行区别--><TimeBasedTriggeringPolicy interval="6" modulate="true"/><SizeBasedTriggeringPolicy size="250 MB"/></Policies></RollingFile></Appenders>
以上介绍了log4j2绝大多数常用的使用方式。
最后补充一下依赖:
要使用log4j2,需要一个api包和一个core包
如果时直接使用log4j2,maven只要导入一个core的jar包即可,比如:
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.11.1</version></dependency>
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ log4j_test ---
[INFO] com.liyao:log4j_test:jar:1.0-SNAPSHOT
[INFO] +- org.apache.logging.log4j:log4j-core:jar:2.11.1:compile
[INFO] | \- org.apache.logging.log4j:log4j-api:jar:2.11.1:compile
如上,两个jar包都被导入了。
那如果想结合slf4j,除了需要以上两个log4j2的jar以外,还需要导入一个log4j-slf4j-impl的桥接依赖和slf4j的api的jar包。
以上4个jar可以通过一个maven依赖导入:
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.11.1</version></dependency>
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ log4j_test ---
[INFO] com.liyao:log4j_test:jar:1.0-SNAPSHOT
[INFO] +- org.apache.logging.log4j:log4j-slf4j-impl:jar:2.11.1:compile
[INFO] | +- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] | +- org.apache.logging.log4j:log4j-api:jar:2.11.1:compile
[INFO] | \- org.apache.logging.log4j:log4j-core:jar:2.11.1:runtime
这是log4j2与log4j在结合slf4j的不同。log4j结合slf4j需要导入slf4j-log4j-xxx的依赖。
以上关于log4j2的介绍就结束了,整理了一周才写完,学无止境!
这篇关于【Java】log4j2 使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!