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(时间对齐)参数。
配置示例:
<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>
代码逻辑解析:
// 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参数);
  • 每次写入日志时,会检查文件大小是否超过阈值,触发滚动。
配置示例:
<RollingFile 
    name="SizeRollingLog"
    fileName="/data/log/app.log"
    filePattern="/data/log/app-%i.log"  <!-- %i为滚动序号123...-->
>
    <PatternLayout charset="UTF-8" pattern="[%-5p %d{HH:mm:ss.SSS}] %m%n"/>
    <Policies>
        <!-- 大小触发策略:文件达到500MB时滚动 -->
        <SizeBasedTriggeringPolicy size="500 MB"/>
    </Policies>
</RollingFile>
代码逻辑解析:
// 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 点)。
配置示例:
<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>
代码逻辑解析:
// 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:应用启动时触发

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

配置示例:
<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 滚动”)。

配置示例:
<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,自动压缩旧文件)。
配置示例(带自动清理):
<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 天前)。

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

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

<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

<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表达式来进行触发的

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

代码

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是基于文件大小触发

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

代码

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时,就会触发滚动操作

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

代码

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是组合多个策略进行触发

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

RolloverStrategy

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

DefaultRolloverStrategy

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

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