0%

log4j2日志文件滚动

Log4j2 日志文件滚动详解:触发策略与滚动策略的完整实践

在生产环境中,日志文件若持续写入会导致单文件体积过大,不仅占用大量磁盘空间,还会导致日志查询和分析效率低下。Log4j2 的RollingFileAppender通过日志滚动机制,按时间、大小等条件自动分割日志文件,是解决这一问题的核心方案。本文详细解析日志滚动的触发策略(TriggeringPolicy)和滚动策略(RolloverStrategy),并结合配置示例说明实战用法。

日志滚动的核心概念

日志滚动的本质是:当满足预设条件时,RollingFileAppender停止向当前日志文件(fileName指定)写入,转而创建新文件继续写入,并对旧文件按规则命名和归档。核心依赖两大组件:

  • 触发策略(TriggeringPolicy):决定 “何时” 触发滚动(如文件达到 1GB、时间到凌晨 0 点);
  • 滚动策略(RolloverStrategy):决定 “如何” 滚动(如旧文件命名规则、保留数量、自动清理)。

触发策略(TriggeringPolicy):何时触发滚动

触发策略定义滚动的触发条件,Log4j2 提供 5 种常用策略,可单独或组合使用(通过<Policies>标签组合)。

1. TimeBasedTriggeringPolicy:基于时间触发(最常用)

核心逻辑:根据日志文件命名格式(filePattern)中的时间粒度,定时触发滚动(如按天、按小时)。

关键特性:
  • 依赖filePattern中的时间占位符(如%d{yyyyMMdd}表示按天,%d{yyyyMMddHH}表示按小时);
  • 滚动时机由时间粒度决定,与日志是否写入无关(即使无日志,到时间也会触发空文件滚动);
  • 支持interval(滚动间隔)和modulate(时间对齐)参数。
配置示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<RollingFile 
name="TimeRollingLog"
fileName="/data/log/app.log" <!-- 当前日志文件 -->
filePattern="/data/log/app-%d{yyyyMMdd}.log" <!-- 滚动后文件(按天命名) -->
>
<PatternLayout charset="UTF-8" pattern="[%-5p %d{HH:mm:ss.SSS}] %m%n"/>
<Policies>
<!-- 时间触发策略:按天滚动 -->
<TimeBasedTriggeringPolicy
interval="1" <!-- 滚动间隔(单位与时间粒度一致,此处1=1天) -->
modulate="true" <!-- 时间对齐(如每天0点滚动,而非启动后24小时) -->
/>
</Policies>
</RollingFile>
代码逻辑解析:
1
2
3
4
5
6
7
8
9
// TimeBasedTriggeringPolicy核心创建逻辑
public static TimeBasedTriggeringPolicy createPolicy(
@PluginAttribute("interval") final String interval, // 默认为1
@PluginAttribute("modulate") final String modulate) { // 默认为false
return newBuilder()
.withInterval(Integers.parseInt(interval, 1)) // 解析间隔,默认1
.withModulate(Boolean.parseBoolean(modulate)) // 解析是否对齐,默认false
.build();
}
  • modulate=true:滚动时间会对齐到自然时间(如每天 0 点、每小时 0 分);
  • modulate=false:滚动时间从应用启动时间开始计算(如启动时间为 10:30,按小时滚动则 11:30、12:30 触发)。

2. SizeBasedTriggeringPolicy:基于文件大小触发

核心逻辑:当当前日志文件大小达到预设阈值时,触发滚动。

关键特性:
  • 阈值单位支持KB/MB/GB(如100 MB1 GB);
  • 默认阈值为10 MB(若未配置size参数);
  • 每次写入日志时,会检查文件大小是否超过阈值,触发滚动。
配置示例:
1
2
3
4
5
6
7
8
9
10
11
<RollingFile 
name="SizeRollingLog"
fileName="/data/log/app.log"
filePattern="/data/log/app-%i.log" <!-- %i为滚动序号(1、2、3...) -->
>
<PatternLayout charset="UTF-8" pattern="[%-5p %d{HH:mm:ss.SSS}] %m%n"/>
<Policies>
<!-- 大小触发策略:文件达到500MB时滚动 -->
<SizeBasedTriggeringPolicy size="500 MB"/>
</Policies>
</RollingFile>
代码逻辑解析:
1
2
3
4
5
6
7
// SizeBasedTriggeringPolicy核心创建逻辑
public static SizeBasedTriggeringPolicy createPolicy(@PluginAttribute("size") final String size) {
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 默认10MB
// 解析size参数,无则用默认值
final long maxSize = size == null ? MAX_FILE_SIZE : FileSize.parse(size, MAX_FILE_SIZE);
return new SizeBasedTriggeringPolicy(maxSize);
}

3. CronTriggeringPolicy:基于 Cron 表达式触发

核心逻辑:通过 Cron 表达式指定精确的滚动时间(如每天凌晨 2 点、每周日午夜),适合需要自定义滚动时机的场景。

关键特性:
  • 不依赖filePattern的时间粒度,完全由 Cron 表达式控制;
  • 内部启动定时线程触发滚动,与日志写入无关(即使无日志也会执行);
  • 默认 Cron 表达式为0 0 0 * * ?(每天凌晨 0 点)。
配置示例:
1
2
3
4
5
6
7
8
9
10
11
<RollingFile 
name="CronRollingLog"
fileName="/data/log/app.log"
filePattern="/data/log/app-%d{yyyyMMdd-HH}.log" <!-- 按“天-小时”命名 -->
>
<PatternLayout charset="UTF-8" pattern="[%-5p %d{HH:mm:ss.SSS}] %m%n"/>
<Policies>
<!-- Cron表达式:每天凌晨2点触发滚动 -->
<CronTriggeringPolicy schedule="0 0 2 * * ?"/>
</Policies>
</RollingFile>
代码逻辑解析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// CronTriggeringPolicy核心创建逻辑
public static CronTriggeringPolicy createPolicy(
@PluginConfiguration final Configuration configuration,
@PluginAttribute("evaluateOnStartup") final String evaluateOnStartup,
@PluginAttribute("schedule") final String schedule) {
private static final String defaultSchedule = "0 0 0 * * ?"; // 默认每天0点
CronExpression cronExpression;
if (schedule == null) {
LOGGER.info("No schedule specified, defaulting to Daily");
cronExpression = getSchedule(defaultSchedule);
} else {
cronExpression = getSchedule(schedule);
if (cronExpression == null) { // 表达式无效时用默认值
LOGGER.error("Invalid expression specified. Defaulting to Daily");
cronExpression = getSchedule(defaultSchedule);
}
}
return new CronTriggeringPolicy(cronExpression, Boolean.parseBoolean(evaluateOnStartup), configuration);
}

4. OnStartupTriggeringPolicy:应用启动时触发

核心逻辑:每次应用启动时,无论当前日志文件大小或时间,均触发一次滚动,避免新日志覆盖旧日志。

配置示例:
1
2
3
4
5
6
7
8
9
10
11
<RollingFile 
name="StartupRollingLog"
fileName="/data/log/app.log"
filePattern="/data/log/app-startup-%d{yyyyMMddHHmmss}.log" <!-- 按启动时间命名 -->
>
<PatternLayout charset="UTF-8" pattern="[%-5p %d{HH:mm:ss.SSS}] %m%n"/>
<Policies>
<!-- 应用启动时触发滚动 -->
<OnStartupTriggeringPolicy/>
</Policies>
</RollingFile>

5. CompositeTriggeringPolicy:组合策略(满足任一条件即触发)

核心逻辑:将多个触发策略组合,只要满足其中一个条件,就触发滚动(如 “按天滚动” 或 “文件达到 1GB 滚动”)。

配置示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<RollingFile 
name="CompositeRollingLog"
fileName="/data/log/app.log"
filePattern="/data/log/app-%d{yyyyMMdd}-%i.log.bz2" <!-- %i=序号,bz2=压缩 -->
>
<PatternLayout charset="UTF-8" pattern="[%-5p %d{yyyy-MM-dd HH:mm:ss.SSS}] %l %m%n"/>
<Policies>
<!-- 时间策略:按天滚动 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 大小策略:文件达到1GB滚动 -->
<SizeBasedTriggeringPolicy size="1 GB"/>
</Policies>
<!-- 滚动策略:控制文件保留数量和压缩 -->
<DefaultRolloverStrategy max="30"/> <!-- 最多保留30个滚动文件 -->
</RollingFile>

滚动策略(RolloverStrategy):如何处理滚动文件

滚动策略定义触发滚动后,如何命名旧文件、是否压缩、保留多少个文件等。Log4j2 默认使用DefaultRolloverStrategy,无需额外配置即可生效,也支持自定义扩展。

1. DefaultRolloverStrategy:默认滚动策略(生产环境首选)

核心功能

  • filePattern的格式命名旧文件(如app-20240830-1.log);
  • 支持日志文件压缩(如bz2gz),减少磁盘占用;
  • 控制滚动文件的最大保留数量(超过则删除最旧文件);
  • 支持自动清理旧日志(结合Delete标签)。
关键参数:
  • max:同一时间粒度下,最大滚动文件数量(默认 7 个);
  • 仅当filePattern中包含%i(序号)时,max才生效;
  • 支持压缩格式(如filePattern后缀为gz/bz2,自动压缩旧文件)。
配置示例(带自动清理):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<RollingFile 
name="AdvancedRollingLog"
fileName="/data/log/app.log"
filePattern="/data/log/archive/app-%d{yyyyMMdd}-%i.log.gz" <!-- 归档目录+压缩 -->
>
<PatternLayout charset="UTF-8" pattern="[%-5p %d{HH:mm:ss.SSS} %t] %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="500 MB"/>
</Policies>
<DefaultRolloverStrategy max="50"> <!-- 每天最多保留50个文件(按大小分割) -->
<!-- 自动删除30天前的归档日志 -->
<Delete basePath="/data/log/archive/" maxDepth="1"> <!-- basePath=归档目录,maxDepth=递归深度 -->
<IfFileName glob="app-*.log.gz"/> <!-- 匹配归档文件 -->
<IfLastModified age="30d"/> <!-- 保留30天内的日志(d=天,h=小时) -->
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
配置说明:
  • basePath:指定清理的根目录(此处为归档日志目录/data/log/archive/);
  • maxDepth:递归清理深度(1表示仅清理当前目录,不包含子目录);
  • IfFileName glob:匹配需要清理的文件格式(如app-*.log.gz);
  • IfLastModified age:清理超过指定时间的文件(如30d表示 30 天前)。

生产环境日志滚动最佳实践

结合上述策略,推荐以下生产环境配置方案,兼顾日志完整性、磁盘占用和查询效率:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<RollingFile 
name="ProdRollingLog"
fileName="/data/log/app/app.log" <!-- 当前日志文件(放在单独目录) -->
filePattern="/data/log/app/archive/app-%d{yyyyMMdd}-%i.log.bz2" <!-- 归档+按天+序号+压缩 -->
>
<!-- 日志格式:包含时间、级别、线程、位置、消息、MDC追踪ID -->
<PatternLayout charset="UTF-8" pattern="[%-5p %d{yyyy-MM-dd HH:mm:ss.SSS} %t %X{traceId}] %l %m%n"/>

<!-- 触发策略:按天滚动 + 1GB大小滚动 -->
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<SizeBasedTriggeringPolicy size="1 GB"/>
</Policies>

<!-- 滚动策略:保留30天归档日志,每天最多50个文件 -->
<DefaultRolloverStrategy max="50">
<Delete basePath="/data/log/app/archive/" maxDepth="1">
<IfFileName glob="app-*.log.bz2"/>
<IfLastModified age="30d"/>
</Delete>
</DefaultRolloverStrategy>

<!-- 过滤:仅输出INFO及以上级别日志 -->
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</RollingFile>

配置优势:

  1. 按天归档:便于按日期查询日志;
  2. 大小限制:避免单文件过大(1GB),提升查询效率;
  3. 压缩存储bz2压缩比高,减少磁盘占用;
  4. 自动清理:保留 30 天日志,防止磁盘占满;
  5. MDC 追踪:日志格式包含%X{traceId},支持分布式链路追踪。

常见问题与解决方案

1. 滚动后旧文件未压缩?

  • 检查filePattern的后缀是否为gz/bz2(如app.log.gz);
  • Log4j2 会根据后缀自动选择压缩算法,无需额外配置。

2. max参数不生效?

  • 确保filePattern中包含%i(序号占位符),如app-%d{yyyyMMdd}-%i.log
  • max仅控制同一时间粒度下的文件数量(如每天最多 50 个),不跨时间粒度(如不限制 30 天内的总文件数)。

3. 自动清理旧日志失败?

  • 检查basePath是否为绝对路径(相对路径基于应用启动目录,易出错);
  • 确保应用对basePath有删除权限;
  • age的单位需与filePattern的时间粒度匹配(如filePattern按天,aged;按小时用h)。

总结

Log4j2 的日志滚动机制通过TriggeringPolicyRolloverStrategy的组合,实现了日志文件的自动分割、归档和清理,是生产环境日志管理的核心功能。关键要点:

  1. 触发策略选择:按时间(TimeBased)+ 大小(SizeBased)组合,兼顾时效性和文件大小;
  2. 滚动策略配置:使用DefaultRolloverStrategy,开启压缩和自动清理,控制磁盘占用;
  3. 格式设计filePattern包含时间、序号和压缩后缀,便于归档和查询

log4j2日志文件滚动

Appender在log4j2中有很多实现类,如ConsoleAppender(插件名称为Console)、FileAppender(插件名称为File)、RollingFileAppender(插件名称为RollingFile)等,在实际的项目中通常使用的都是RollingFileAppender,也就是日志文件滚动更新的Appender

1
2
3
4
5
6
7
8
9
<RollingFile name="file" fileName="/data/log/app.log"
filePattern="/data/log/app.log.%d{yyyyMMdd}.%i.bz2">
<PatternLayout charset="UTF-8" pattern="[%-5p %d{yyyy-MM-dd HH:mm:ss.SSS}] %l [%m]%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1024 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>

其滚动依赖于TriggeringPolicy(触发策略)和RolloverStrategy(滚动更新策略)

TriggeringPolicy

TriggeringPolicy为触发策略,决定了何时触发日志文件的滚动。常见策略有CronTriggeringPolicy、OnStartupTriggeringPolicy、SizeBasedTriggeringPolicy、TimeBasedTriggeringPolicy、CompositeTriggeringPolicy

CronTriggeringPolicy

CronTriggeringPolicy是基于Cron表达式来进行触发的

1
2
<!-- 根据cron来进行触发 -->
<CronTriggeringPolicy schedule="0 0 0 * * ?"/>

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static CronTriggeringPolicy createPolicy(@PluginConfiguration final Configuration configuration,
@PluginAttribute("evaluateOnStartup") final String evaluateOnStartup,
@PluginAttribute("schedule") final String schedule) {
// private static final String defaultSchedule = "0 0 0 * * ?";
CronExpression cronExpression;
final boolean checkOnStartup = Boolean.parseBoolean(evaluateOnStartup);
if (schedule == null) {
LOGGER.info("No schedule specified, defaulting to Daily");
cronExpression = getSchedule(defaultSchedule);
} else {
cronExpression = getSchedule(schedule);
if (cronExpression == null) {
LOGGER.error("Invalid expression specified. Defaulting to Daily");
cronExpression = getSchedule(defaultSchedule);
}
}
return new CronTriggeringPolicy(cronExpression, checkOnStartup, configuration);
}

该策略与SizeBasedTriggeringPolicy、TimeBasedTriggeringPolicy不同,不管日志是否写入,而是新开启一个定时线程来进行管理,而不是根据isTriggeringEvent方法来判断是否触发的

OnStartupTriggeringPolicy

OnStartupTriggeringPolicy是在JVM启动时触发

SizeBasedTriggeringPolicy

SizeBasedTriggeringPolicy是基于文件大小触发

1
2
<!-- 当达到指定size时触发滚动操作,可以用KB/MB/GB作为单位,默认是10M -->
<SizeBasedTriggeringPolicy size="1024 MB"/>

代码

1
2
3
4
5
public static SizeBasedTriggeringPolicy createPolicy(@PluginAttribute("size") final String size) {
// private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // let 10 MB the default max size
final long maxSize = size == null ? MAX_FILE_SIZE : FileSize.parse(size, MAX_FILE_SIZE);
return new SizeBasedTriggeringPolicy(maxSize);
}

在日志写入时会触发isTriggeringEvent方法来判断是否需要滚动

TimeBasedTriggeringPolicy

TimeBasedTriggeringPolicy是基于时间触发,与日志filePattern中的时间有关,当日志文件名中的pattern不再符合filePattern中的pattern时,就会触发滚动操作。如我配置的filePattern="/data/log/app.log.%d{yyyyMMdd},文件名为app.log.20240830.log,当时间到达20240831时,就会触发滚动操作

1
2
3
4
5
<!-- 
interval 默认1,时间间隔,单位是filePattern中设置的最小时间粒度,如我上述配置%d{yyyyMMdd},时间粒度就是天,每天触发一次
modulate 默认false,指明是否对interval进行调节。若modulate为true,会以0为开始对interval进行偏移计算
-->
<TimeBasedTriggeringPolicy/>

代码

1
2
3
4
5
6
7
8
public static TimeBasedTriggeringPolicy createPolicy(
@PluginAttribute("interval") final String interval,
@PluginAttribute("modulate") final String modulate) {
return newBuilder()
.withInterval(Integers.parseInt(interval, 1))
.withModulate(Boolean.parseBoolean(modulate))
.build();
}

在日志写入时会触发isTriggeringEvent方法来判断是否需要滚动

CompositeTriggeringPolicy

CompositeTriggeringPolicy是组合多个策略进行触发

1
2
3
4
5
<!-- 复合策略,将多个策略放到Policies中,满足一个条件就会触发滚动 -->
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1024 MB"/>
</Policies>

RolloverStrategy

RolloverStrategy为滚动更新策略,决定了当触发日志文件的滚动时,如何进行文件的滚动。log4j2提供了默认的DefaultRolloverStrategy

DefaultRolloverStrategy

DefaultRolloverStrategy是提供的默认更新策略,即使不进行配置,也会使用该策略,默认max为7

1
2
3
4
<!-- 默认max为7,表示最大保留的日志个数,超过则删除旧的日志
与filePattern中的%i配置配合,如果没有使用%i则该max配置无效
-->
<DefaultRolloverStrategy max="1000"/>

欢迎关注我的其它发布渠道

表情 | 预览
Powered By Valine
v1.3.10