在 Linux 系统中,logrotate 是几乎所有发行版默认提供的日志轮转工具。但在实际使用中,很多配置项“看起来懂了”,实际行为却经常出乎意料。
本文基于实际验证与行为分析,逐条解析一组典型的 logrotate 配置,重点说明:
配置项真正的语义
logrotate 的执行模型
常见的误解与反直觉行为
不同选项组合后的真实效果
示例配置
/path/to/logs/*.log {
daily
dateyesterday
dateext
rotate 15
nocopytruncate
nocreate
compress
notifempty
missingok
}下面将围绕这份配置展开说明。
一、daily:并不是“每天 0 点轮转”
1.1 常见误解
很多人理解 daily 为:
logrotate 每天 00:00 执行一次
或“按日志时间每天切一个文件”
这两种理解都是错误的。
1.2 logrotate 的真实执行模型
logrotate 本身不具备调度能力,它的工作流程是:
由
cron/systemd timer触发执行读取状态文件
/var/lib/logrotate/status对比:
上一次轮转时间
当前执行时间
判断是否满足轮转条件
对于 daily,判断条件只有一个:
当前日期 ≠ 状态文件中记录的日期
1.3 实际验证结果
同一天内多次执行
logrotate只有第一次会发生轮转
后续执行直接跳过
状态文件不会发生变化
验证:
同⼀天内多次执⾏:


结果:
每天仅第⼀次发⽣轮转
第⼆次直接跳过
状态⽂件⽆变化

结论:
daily是一种「按天去重的轮转条件」,而不是调度指令。
1.4 与 hourly 的对比
hourly是 logrotate 支持的最小轮转频率同一小时内多次执行只会轮转一次
跨小时后再次执行,才会触发新一轮轮转
验证:
修改配置为hourly,第⼀次执⾏logrotate正常轮转并记录轮转时间到状态⽂件
同⼀⼩时重复执⾏logrotate,提⽰已经轮转过⽆需轮转

修改时间到下⼀⼩时,执⾏logrotate正常轮转

二、dateext + dateyesterday:修正文件名的时间语义
2.1 仅使用 dateext 的问题
如果 logrotate 在凌晨执行,例如:
2025-12-26 01:00轮转结果为:
app.log-2025-12-26但该文件内容几乎全部来自 12-25 的日志。
验证:
单独使⽤dateext,⽇志轮转⽂件命名为执⾏时间当天⽇期

文件名日期 ≠ 实际日志覆盖时间
2.2 dateyesterday 的作用
dateext
dateyesterday启用后:
文件名使用 日志实际覆盖的日期
与 logrotate 执行时间解耦

2.3 与 daily 的组合语义
daily
dateext
dateyesterday这一组合的真实含义是:
明确以“按自然日分析日志”为目标设计的轮转策略
三、rotate 15:不是“保留 15 天”
3.1 rotate 的真实含义
rotate 15表示:
最多保留 15 次成功轮转产生的历史文件
而不是 15 天
3.2 边界行为说明
如果某天日志为空:
notifempty生效不发生轮转
不会消耗 rotate 计数
删除旧文件的时机:
在新轮转完成之后
删除超出数量限制的最旧文件
验证:
修改rotate 5

当前已轮转5次,grafana因为空⽂件存在2个轮转⽂件,ng存在5个⽂件

下次轮转删除了ng 20251201的⽂件,grafana还是2个轮转⽂件

四、nocopytruncate:选择哪种轮转机制
4.1 两种轮转模型对比
copytruncate
拷贝当前日志为历史文件
清空原文件
inode 不变
适合:进程持续写同一个文件名

nocopytruncate
通过
rename完成轮转不清空正在写入的文件
需要写日志的进程能自行切换文件

4.2 使用 nocopytruncate 的典型场景
日志文件名本身带时间(如按小时生成)
写入进程在时间边界会自动关闭旧文件
不需要保持 inode 不变
五、nocreate:logrotate 是否负责创建新文件
nocreate含义:
logrotate 不负责创建新的日志文件
新日志文件由业务进程自行创建
实践验证表明:
即使未启用
nocreate在
nocopytruncate场景下logrotate 也不会主动创建空日志文件

因此在多数场景下:
nocreate更像是责任边界的明确声明
六、compress:历史日志压缩行为
compress行为说明:
轮转完成后 立即压缩历史文件
不影响当前正在写入的新日志
默认使用
gzip可通过
compresscmd指定其他压缩工具
验证:
gzip压缩轮转⽂件

如果不使⽤,不压缩保留带⽇期轮转⽂件


七、notifempty / missingok:边界条件控制
7.1 notifempty
空文件不轮转
不记录状态
避免产生无意义的历史日志
验证:
使用该配置


不使用该配置

7.2 missingok
日志文件不存在时不报错
避免整个 logrotate 任务失败
验证:
使用该配置

不使⽤该配置

八、一个非常反直觉但重要的机制
logrotate 的“首次只记录状态”行为
logrotate 对一个从未见过的日志文件:
第一次执行:
不轮转
只在状态文件中记录时间
第二次执行:
才会根据配置判断是否轮转
这意味着:
新生成的日志文件,第一次不会被轮转
这一点在日志动态生成、服务重启场景下尤为重要。
验证:
当前状态⽂件

触发服务重启让pingpong产⽣⼀个新的⽇志⽂件

修改时间到第⼆天凌晨,触发logrotate,看到只有⼀个pingpong的⽇志被轮转,新产⽣的⼀个⽇ 志只记录状态未被轮转




总结
logrotate 的配置项本身并不复杂,但:
语义依赖状态文件
行为依赖执行时间
多个参数组合后才形成真实效果
理解它的关键不在于“记住参数”,而在于:
理解 logrotate 是如何做判断的
希望这篇文章能帮你在设计日志轮转策略时,少踩一些已经被踩过的坑。