官网使用手册:https://logging.apache.org/log4j/2.x/manual/
使用
Log4j2 的 Maven 依赖:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.0</version>
</dependency>
Maven 依赖的名称还是叫 log4j,只是在 version 中体现出了新版本。依赖关系如下:
我们写一个示例:
public class Log4j2Test {
private static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
public static void main(String[] args) {
LOGGER.trace("Hello trace Log4j2!");
LOGGER.debug("Hello debug Log4j2!");
LOGGER.info("Hello info Log4j2!");
LOGGER.warn("Hello warn Log4j2!");
LOGGER.error("Hello error Log4j2!");
}
}
应用启动时 Log4j2 会去寻找 4 种类型的配置文件,后缀分别是 properties、yaml、json 和 xml。前缀是 log4j2-test 或者 log4j2。通常我们使用名为 log4j2.xml 的配置文件。
如果是配合 Slf4j 的 API 使用也是可以的,只需要做好适配即可。不过如果是新系统的话,建议直接使用 Log4j2 的 API,因为可以享受所有 Log4j2 的功能,如果使用 Slf4j 在 API 上会有语法限制。
配置文件
Log4j2 的配置文件格式和 Logback 有点相似,基本的结构为
<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns:xi="http://www.w3.org/2001/XInclude"
status="warn" name="XInclude">
<Appenders>
<RollingFile name="File" fileName="logs/app.log" filePattern="logs/archives/app-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] %-40.40c{1.} : %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy />
<!-- 一天内大于size就单独分隔 -->
<SizeBasedTriggeringPolicy size="1 GB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
1. Appenders
Appenders 就是配置日志的输出目的地,内部可以包含多个不同的输出地。与 Logback 不同的是,它是通过标签名来确定要输出的目的地的,这样语法上更加简洁。
<Console name="Console">
<RollingFile name="File">
<!-- 等同于logback中的 -->
<appender name = "Console" class= "ch.qos.logback.core.ConsoleAppender">
<appender name = "File" class= "ch.qos.logback.core.rolling.RollingFileAppender">
2. 自动重载配置
Logback 支持自动重载配置,Log4j2 当然也支持,想要启用这个功能也非常简单,只需要在 Configuration 元素上添加 monitorInterval 属性即可。
<Configuration monitorInterval="30">
...
</Configuration>
注意:最小间隔为 5 秒,上例中的意思是每隔 30 秒检查配置文件中的配置是否更改。
3. 异步输出
与 Log4j 相比,Log4j2 的异步输出日志性能非常强劲,但在具体使用时我们需要了解不同配置的含义:
全异步模式:
可通过如下系统配置开启全异步(all async)模式:
-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
这样,所有日志记录器都是异步的,在 XML 配置文件中只需要使用普通的
混合异步模式:
通过在 XML 配置文件中使用
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- Async Loggers will auto-flush in batches, so switch off immediateFlush. -->
<RandomAccessFile name="RandomAccessFile" fileName="asyncWithLocation.log"
immediateFlush="false" append="false">
<PatternLayout>
<Pattern>%d %p %class{1.} [%t] %location %m %ex%n</Pattern>
</PatternLayout>
</RandomAccessFile>
</Appenders>
<Loggers>
<!-- pattern layout actually uses location, so we need to include it -->
<AsyncLogger name="com.foo.Bar" level="trace" includeLocation="true">
<AppenderRef ref="RandomAccessFile"/>
</AsyncLogger>
<Root level="info" includeLocation="true">
<AppenderRef ref="RandomAccessFile"/>
</Root>
</Loggers>
</Configuration>
4. 滚动更新策略
RollingFileAppender 是 Log4j2 中的一种能够实现日志文件滚动更新的 Appender。滚动的意思是当满足一定条件(如文件达到指定大小,达到指定的时间)后就重命名原日志文件进行归档,并生成新的日志文件用于 log 写入。如果还设置了一定时间内允许归档的日志文件的最大数量,将对过旧的日志文件进行删除操作。
RollingFile 实现日志文件滚动更新,依赖于 TriggeringPolicy 和 RolloverStrategy。前者为触发策略,其决定了何时触发日志文件的滚动,即 When;后者为滚动更新策略,其决定了当触发了日志文件的滚动时,如何进行文件的滚动更新,即 How。
Log4j2 提供了默认的滚动策略:DefaultRolloverStrategy。一般情况下,采用默认的过渡策略即可,因为它已经足够强大。下面通过一个 log4j2.xml 文件示例来简单了解 RollingFile 的配置:
<RollingFile name="RollingFileWarn" fileName="logs/app.log"
filePattern="logs/app.%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd} [%t] %-5level %l %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="100MB" />
</Policies>
<DefaultRolloverStrategy max="20" />
</RollingFile>
- fileName 指定日志文件的位置和文件名称(如果文件或文件所在的目录不存在,会创建文件)
- filePattern 指定触发滚动更新时,文件的重命名规则
上述配置文件中配置了一个 RollingFile,日志写入 logs/app.log 文件中,每经过 1 天或者当文件大小到达 250M 时,按照 app.2021-08-01-1.log 的格式对 app.log 进行重命名、归档,并生成新文件用于写日志。
下面我们重点分析下触发策略,Log4j2 提供的触发策略主要包含:
- 基于 cron 表达式的 CronTriggeringPolicy
- 基于文件大小的 SizeBasedTriggeringPolicy
- 基于时间的 TimeBasedTriggeringPolicy
- 混合多个触发策略的 CompositeTriggeringPolicy,比如同时基于文件大小和时间
- 默认提供的 DefaultRolloverStrategy
4.1 SizeBasedTriggeringPolicy
规定了当日志文件达到了指定的 size 时,触发滚动更新操作。size 参数可以用 KB、MB、GB 等做后缀来指定具体的字节数,如 20MB。
<SizeBasedTriggeringPolicy size="250MB"/>
4.2 TimeBasedTriggeringPolicy
规定了当日志文件名中的 date/time pattern 不再符合 filePattern 中的 date/time pattern 时,触发滚动更新操作。比如,filePattern 指定文件重命名规则为 app-%d{yyyy-MM-dd HH}.log,文件名为 app-2017-08-25 11.log,当时间达到 2017 年 8 月 25 日中午 12 点(2017-08-25 12)时将触发滚动更新操作。
4.3 CompositeTriggeringPolicy
将多个 TriggeringPolicy 放到 Policies 标签中以表示使用复合策略:
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
如上,同时使用了 TimeBasedTriggeringPolicy 和 SizeBasedTriggeringPolicy,只要有其中一个条件满足,就会触发滚动更新。
4.4 DefaultRolloverStrategy
DefaultRolloverStrategy 是 Log4j2 提供的默认的 rollover 策略,即使没有在 log4j2.xml 中显式指明。该策略提供了一个 max 参数用于指定计数器的最大值。一旦计数器达到了最大值,过旧的日志文件将被删除。max 参数要与 filePattern 中的计数器 %i 配合起作用,具体作用方式与 filePattern 的配置密切相关。
如果 filePattern 中仅含有 date/time pattern,每次 rollover 时,将用当前的日期和时间替换文件中的日期格式对文件进行重命名。max 参数此时不起作用。
如果 filePattern 中仅含有整数计数器(即 %i),每次 rollover 时,文件重命名时的计数器将每次加 1(初始值为 1),若达到 max 的值,将删除旧的文件。
如果 filePattern 中既含有 date/time pattern,又含有 %i,每次 rollover 时,计数器将每次加 1,若达到 max 的值将删除旧的文件,直到 data/time pattern 不再符合,被替换为当前的日期和时间,计数器再从 1 开始计数。
5. 配置总览
本配置文件的目标是将不同级别的日志输出到不同文件,最大 100MB 一个文件,文件数据达到最大值时,旧数据会被压缩并放进指定文件夹,最多存放 20 个文件。
<!-- status:用于设置log4j2自身内部的信息输出,可以不设置 -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- status : 这个用于设置log4j2自身内部的信息输出,可以不设置-->
<Configuration status="error">
<!-- 配置日志文件输出目录,此配置将日志输出到根目录下的指定文件夹 -->
<Properties>
<Property name="fileDir">/logs/log4j2</Property>
<Property name="fileHistory">/logs/log4j2/history</Property>
</Properties>
<Appenders>
<!-- 优先级从高到低分别是 OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL -->
<!-- 单词解释:Match:匹配 DENY:拒绝 Mismatch:不匹配 ACCEPT:接受 -->
<!-- DENY,日志将立即被抛弃不再经过其他过滤器;NEUTRAL,有序列表里的下个过滤器过接着处理日志;ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
<!-- 输出日志的格式
%d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间
%t 输出当前线程名称
%-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
%logger 输出logger名称,因为Root Logger没有名称,所以没有输出
%msg 日志文本
%n 换行
其他常用的占位符有:
%F 输出所在的类文件名,如Client.java
%L 输出行号
%M 输出所在方法名
%l 输出语句所在的行数, 包括类名、方法名、文件名、行数
-->
<!--这个输出控制台的配置,这里输出all信息到System.out -->
<console name="Console" target="SYSTEM_OUT">
<!-- 输出日志的格式 -->
<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
</console>
<!--这个输出文件的配置,这里输出info信息到junbao_info.log -->
<RollingFile name="RollingFileInfo" fileName="${fileDir}/info.log" filePattern="${fileHistory}/info/%d{yyyy-MM-dd}-%i.log">
<!-- 此Filter意思是,只输出info级别的数据 DENY,日志将立即被抛弃不再经过其他过滤器;NEUTRAL,有序列表里的下个过滤器过接着处理日志;
ACCEPT,日志会被立即处理,不再经过剩余过滤器。 -->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
<Policies>
<!-- 如果启用此配置,则日志会按文件名生成新文件, 即如果filePattern配置的日期格式为 %d{yyyy-MM-dd HH}
,则每小时生成一个压缩文件, 如果filePattern配置的日期格式为 %d{yyyy-MM-dd} ,则天生成一个压缩文件,默认为1 -->
<TimeBasedTriggeringPolicy />
<!-- 每个日志文件最大100MB,超过100MB生产新的文件 ; -->
<SizeBasedTriggeringPolicy size="100MB" />
</Policies>
<!--文件夹下最多的文件个数-->
<DefaultRolloverStrategy max="20" />
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${fileDir}/warn.log" filePattern="${fileHistory}/warn/%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100MB" />
</Policies>
<DefaultRolloverStrategy max="20" />
</RollingFile>
<RollingFile name="RollingFileError" fileName="${fileDir}/error.log" filePattern="${fileHistory}/error/%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="100MB" />
</Policies>
<DefaultRolloverStrategy max="20" />
</RollingFile>
</Appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<Loggers>
<!--同步输出info级以上的日志信息-->
<!-- <root level="info" includeLocation="true">
<appender-ref ref="Console" />
</root> -->
<!--全异步输出info级以上的日志信息-->
<asyncRoot level="info" includeLocation="true">
<appender-ref ref="Console" />
<appender-ref ref="RollingFileInfo" />
</asyncRoot>
</Loggers>
</Configuration>