4、日志切割与轮转:Linux logrotate配置、按大小/时间切割策略、避免磁盘写满的实战经验
日志这东西,不炸的时候你觉得它可有可无。一旦磁盘满了,服务挂了,你才会发现——嗯,原来它一直在默默“吃”你的硬盘。
我见过太多线上事故了。有一次,一个业务模块的日志一天写了 50GB,三天后磁盘报警,结果没人处理。第四天凌晨,整个集群的日志分区写满,所有服务全部 OOM 退出。你说冤不冤?其实只要配好 logrotate,这种事完全可以避免。
今天我们就来聊聊日志切割与轮转。说白了,就是让日志“长不大”。
4.1 为什么需要日志切割?
先问一个问题:你的应用日志,写多久会占满磁盘?
我见过很多团队,日志配置是“永远不切割”。结果呢?一个单文件能长到几十 GB。别说 grep 查问题了,连 vim 打开都卡半天。更可怕的是,磁盘满了之后,写日志的操作会阻塞,甚至导致进程 hang 死。
所以,日志切割的核心目的就两个:
- 控制磁盘使用量——不让日志无限制增长
- 方便排查问题——按时间或大小切分,查找时更高效
核心原则:日志切割不是“切着玩”,而是为了在“保留足够历史”和“节省磁盘空间”之间找到平衡点。
4.2 Linux logrotate 基础配置
logrotate 是 Linux 上最常用的日志轮转工具。它本身不产生日志,只负责“管理”日志文件。
它的配置文件一般在 /etc/logrotate.conf 和 /etc/logrotate.d/ 目录下。我个人习惯把每个应用的配置单独放在 /etc/logrotate.d/ 里,方便管理。
来看一个最基础的配置示例:
/var/log/myapp/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
}
解释一下每个参数:
| 参数 | 含义 |
|---|---|
| daily | 每天切割一次 |
| rotate 7 | 保留最近 7 个切割后的文件 |
| compress | 切割后压缩(默认 gzip) |
| delaycompress | 延迟压缩,保留上一个未压缩的版本 |
| missingok | 日志文件不存在时不报错 |
| notifempty | 日志为空时不切割 |
| copytruncate | 先复制再清空原文件,适合持续写入的进程 |
小提示:如果你的应用是持续写日志的(比如 Java 的 log4j、Python 的 logging),建议用 copytruncate。如果用默认的 create 方式,需要应用支持重新打开日志文件(比如发送 SIGHUP 信号)。
4.3 按大小切割 vs 按时间切割
这是大家最常纠结的问题。我直接说结论:
- 按时间切割(daily/weekly/monthly):适合日志量相对稳定的场景。比如每天 1GB,你每天切一次,保留 7 天,总共 7GB。好预测,好管理。
- 按大小切割(size 100M):适合日志量波动大的场景。比如平时一天 100MB,但搞活动时一天能写 10GB。按时间切的话,一个文件可能大到离谱。
我个人更推荐 两者结合。比如:
/var/log/myapp/*.log {
daily
size 500M
rotate 14
compress
missingok
notifempty
copytruncate
}
这个配置的意思是:每天检查一次,如果日志文件超过 500MB 就提前切割。如果没超过,就等到第二天再切。这样既控制了单文件大小,又保证了每天至少切一次。
注意:size 参数的单位是 M 或 G,不要写成 MB 或 GB。我见过有人写 size 100MB,结果 logrotate 直接忽略了这个配置。
4.4 实战经验:避免磁盘写满的 5 个要点
我在项目中踩过不少坑,总结下来,这 5 点最重要:
- 设置 maxage:除了 rotate 数量,还可以用
maxage 30来限制文件的最长保留天数。即使 rotate 数量没到,超过 30 天的日志也会被删除。 - 监控日志目录大小:不要只依赖 logrotate。我习惯在监控系统里加一个“日志目录大小”的指标,超过阈值就告警。
- 测试配置:每次修改 logrotate 配置后,用
logrotate -d /etc/logrotate.d/myapp做一次 dry-run 测试。别直接上生产。 - 注意权限:logrotate 默认以 root 运行。如果你的日志文件属于某个普通用户,记得在配置里加
su user group,否则可能切不动。 - 处理大日志的切割:如果一个日志文件已经 50GB 了,logrotate 切割时会先复制再清空,这个过程可能很慢。我建议在日志量还小的时候就配置好切割策略。
避坑指南:我曾经遇到过一个 case——日志文件被 logrotate 切了,但应用进程还在往旧文件里写。原因是应用打开文件时用的是文件描述符,logrotate 的 copytruncate 虽然清空了原文件,但文件描述符没变,应用继续写。解决方案是:要么用 copytruncate,要么在切割后给应用发 SIGHUP 信号。
4.5 高级配置:按日期命名与压缩策略
默认情况下,logrotate 切割后的文件命名是 xxx.log.1、xxx.log.2.gz 这种。说实话,不太直观。我更喜欢用 dateext 和 dateformat:
/var/log/myapp/*.log {
daily
rotate 30
compress
dateext
dateformat -%Y%m%d
missingok
notifempty
copytruncate
}
这样切割后的文件就是 xxx.log-20250315.gz,一眼就能看出是哪天的日志。
另外,压缩策略也值得注意。默认的 gzip 压缩率不错,但速度慢。如果你的日志量特别大,可以考虑用 compresscmd /usr/bin/zstd 换成 zstd 压缩,速度快很多。
4.6 如何验证 logrotate 是否生效?
配置完了,怎么知道它到底有没有在干活?
我一般用这几个方法:
- 手动触发一次:
logrotate -f /etc/logrotate.d/myapp,强制切割一次,看看文件有没有变化。 - 查看状态文件:
cat /var/lib/logrotate/status,里面记录了每个日志文件上次切割的时间。 - 检查 cron 日志:logrotate 通常由 cron 驱动,可以看
/var/log/cron里有没有 logrotate 的执行记录。
小技巧:如果你发现 logrotate 没按预期执行,先检查 cron 服务是否在运行。很多次我排查到最后,发现是 cron 挂了。
4.7 总结
日志切割这件事,说难不难,说简单也不简单。关键在于:
- 提前规划好切割策略
- 测试验证后再上线
- 配合监控,别等磁盘满了才想起来
嗯,今天就聊到这里。下一章我们会讲日志采集与传输,到时候聊聊怎么把切好的日志安全地送到中心化存储里去。