【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

相关文章

使用SpringBoot+InfluxDB实现高效数据存储与查询

《使用SpringBoot+InfluxDB实现高效数据存储与查询》InfluxDB是一个开源的时间序列数据库,特别适合处理带有时间戳的监控数据、指标数据等,下面详细介绍如何在SpringBoot项目... 目录1、项目介绍2、 InfluxDB 介绍3、Spring Boot 配置 InfluxDB4、I

基于Java和FFmpeg实现视频压缩和剪辑功能

《基于Java和FFmpeg实现视频压缩和剪辑功能》在视频处理开发中,压缩和剪辑是常见的需求,本文将介绍如何使用Java结合FFmpeg实现视频压缩和剪辑功能,同时去除数据库操作,仅专注于视频处理,需... 目录引言1. 环境准备1.1 项目依赖1.2 安装 FFmpeg2. 视频压缩功能实现2.1 主要功

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

使用Python实现无损放大图片功能

《使用Python实现无损放大图片功能》本文介绍了如何使用Python的Pillow库进行无损图片放大,区分了JPEG和PNG格式在放大过程中的特点,并给出了示例代码,JPEG格式可能受压缩影响,需先... 目录一、什么是无损放大?二、实现方法步骤1:读取图片步骤2:无损放大图片步骤3:保存图片三、示php

Spring-DI依赖注入全过程

《Spring-DI依赖注入全过程》SpringDI是核心特性,通过容器管理依赖注入,降低耦合度,实现方式包括组件扫描、构造器/设值/字段注入、自动装配及作用域配置,支持灵活的依赖管理与生命周期控制,... 目录1. 什么是Spring DI?2.Spring如何做的DI3.总结1. 什么是Spring D

使用Python实现一个简易计算器的新手指南

《使用Python实现一个简易计算器的新手指南》计算器是编程入门的经典项目,它涵盖了变量、输入输出、条件判断等核心编程概念,通过这个小项目,可以快速掌握Python的基础语法,并为后续更复杂的项目打下... 目录准备工作基础概念解析分步实现计算器第一步:获取用户输入第二步:实现基本运算第三步:显示计算结果进

spring AMQP代码生成rabbitmq的exchange and queue教程

《springAMQP代码生成rabbitmq的exchangeandqueue教程》使用SpringAMQP代码直接创建RabbitMQexchange和queue,并确保绑定关系自动成立,简... 目录spring AMQP代码生成rabbitmq的exchange and 编程queue执行结果总结s

Java调用Python脚本实现HelloWorld的示例详解

《Java调用Python脚本实现HelloWorld的示例详解》作为程序员,我们经常会遇到需要在Java项目中调用Python脚本的场景,下面我们来看看如何从基础到进阶,一步步实现Java与Pyth... 目录一、环境准备二、基础调用:使用 Runtime.exec()2.1 实现步骤2.2 代码解析三、

聊聊springboot中如何自定义消息转换器

《聊聊springboot中如何自定义消息转换器》SpringBoot通过HttpMessageConverter处理HTTP数据转换,支持多种媒体类型,接下来通过本文给大家介绍springboot中... 目录核心接口springboot默认提供的转换器如何自定义消息转换器Spring Boot 中的消息

python之uv使用详解

《python之uv使用详解》文章介绍uv在Ubuntu上用于Python项目管理,涵盖安装、初始化、依赖管理、运行调试及Docker应用,强调CI中使用--locked确保依赖一致性... 目录安装与更新standalonepip 安装创建php以及初始化项目依赖管理uv run直接在命令行运行pytho