【Java】log4j2 使用

2024-05-24 21:18
文章标签 java 使用 log4j2

本文主要是介绍【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 使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/999558

相关文章

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他