4、日志轮换实战:按大小轮换、按时间轮换、压缩与归档、保留策略
日志轮换,说白了就是给日志文件“计划生育”。
服务器跑久了,日志文件能大到吓人。我见过最夸张的一次,一个没配轮换的 Tomcat 应用,catalina.out 撑到了 80 多 G,直接把磁盘塞爆,服务全挂了。从那以后,我接手任何系统,第一件事就是检查日志轮换配了没。
这一章,咱们把 logrotate 的四个核心能力拆开揉碎:按大小切、按时间切、压缩归档、保留几份。你跟着走一遍,以后配轮换脚本心里就有底了。
核心原则:日志轮换不是备份,是“切割 + 清理”。目的是让日志文件保持可控大小,同时保留最近一段时间或一定数量的历史记录,方便排查问题。
4.1 按大小轮换:logrotate 的“体重秤”
按大小轮换,就是给日志文件设一个“体重上限”。超过这个值,logrotate 就把它切掉。
我个人习惯用 size 指令,单位支持 K、M、G。比如 size 100M,表示日志超过 100MB 就轮换。
小技巧:还有一个 maxsize 指令,它和 size 的区别在于:size 是“超过就切”,maxsize 是“在时间轮换周期内,如果超过这个大小,也触发轮换”。说白了,maxsize 是给时间轮换加了一道“保险”。
来看一个典型的按大小轮换配置:
/var/log/myapp/*.log {
size 100M
rotate 5
compress
missingok
notifempty
copytruncate
}
这里我解释几个关键点:
- size 100M:日志文件超过 100MB 就触发轮换。
- rotate 5:保留最近 5 份历史日志。
- compress:轮换后自动用 gzip 压缩。
- copytruncate:这个指令很实用。它先复制日志内容到新文件,然后清空原文件。这样应用程序不需要重启,因为文件句柄没变。我建议在 Nginx、Tomcat 这类不自动 reopen 日志的应用上一定要加这个。
注意:copytruncate 有一个小坑——在复制和截断之间,可能会有少量日志丢失。不过对于大多数场景,这点丢失可以接受。如果你要求零丢失,那就用 create 指令配合应用自身的日志重载信号。
4.2 按时间轮换:每日/每周的“闹钟”
按时间轮换,就是定好时间点,到点就切。最常用的是 daily 和 weekly。
你想想看,为什么需要按时间轮换?
- 排查方便:出问题时,你直接说“看昨天的日志”,而不是“看第 7 个轮换文件”。
- 规律性强:每天凌晨切一次,日志文件大小基本稳定,磁盘占用可预测。
- 配合监控:很多监控系统按天拉取日志,时间轮换天然对齐。
每日轮换的经典配置:
/var/log/nginx/access.log {
daily
rotate 30
compress
delaycompress
dateext
dateformat -%Y%m%d
missingok
notifempty
postrotate
/usr/sbin/nginx -s reopen
endscript
}
这里有几个我特别想强调的点:
- delaycompress:延迟压缩。轮换当天不压缩,第二天再压缩。为什么?因为运维同学早上排查问题时,可能还需要看昨天的日志,压缩了还得解压,麻烦。我踩过这个坑,后来就一直用
delaycompress。 - dateext:用日期作为轮换文件的后缀,而不是默认的数字序号。比如
access.log-20250410,一眼就知道是哪天的日志。 - postrotate:轮换后执行脚本。这里给 Nginx 发
reopen信号,让它重新打开日志文件句柄。如果不做这一步,Nginx 会继续往旧文件里写。
每周轮换类似,把 daily 换成 weekly 就行。我个人习惯在日志量不大的系统上用 weekly,比如一些内部管理工具。
4.3 压缩与归档:gzip 和 tar 的取舍
日志轮换后不压缩,那磁盘还是扛不住。logrotate 默认用 gzip 压缩,效果不错。
| 压缩方式 | 压缩比 | 速度 | 适用场景 |
|---|---|---|---|
| gzip(默认) | 中等(约 70-80%) | 快 | 日常日志轮换,最常用 |
| bzip2 | 高(约 80-90%) | 慢 | 归档日志,不常查看 |
| xz | 极高(约 90-95%) | 很慢 | 长期冷存储 |
| tar(无压缩) | 无 | 极快 | 需要保留原始格式,或后续统一压缩 |
logrotate 默认用 gzip,你如果想换,用 compresscmd 指定:
/var/log/myapp/*.log {
weekly
rotate 12
compress
compresscmd /usr/bin/bzip2
compressext .bz2
}
至于 tar 归档,logrotate 本身不直接支持 tar 打包。我一般这样处理:
- 先用 logrotate 做轮换和 gzip 压缩。
- 再用一个 cron 脚本,把超过 7 天的压缩日志打包成 tar 归档,然后删除原文件。
举个例子:
#!/bin/bash
# 每周日打包上周的日志
cd /var/log/myapp
find . -name "*.gz" -mtime +7 -exec tar -rvf weekly_archive.tar {} \;
find . -name "*.gz" -mtime +7 -delete
避坑指南:我曾经在压缩日志时,忘记考虑压缩后的文件名变化。logrotate 默认把 access.log.1 压缩成 access.log.1.gz,但如果你用了 dateext,文件名是 access.log-20250410.gz。写脚本处理时,一定要确认命名规则。
4.4 保留策略:留多少份才够?
保留策略,就是决定“历史日志留多久”。logrotate 用 rotate 指令控制保留份数。
比如 rotate 7,表示保留最近 7 份轮换后的日志。第 8 份生成时,最老的那份会被删除。
除了份数,还有时间维度的保留:
- rotate N:保留 N 份轮换文件。
- maxage DAYS:保留最近 N 天的日志,超过天数的直接删。
举个例子,我想保留最近 30 天的日志,但每天可能轮换多次(按大小触发),用 rotate 不好算。这时候用 maxage 更合适:
/var/log/myapp/*.log {
size 50M
maxage 30
compress
missingok
notifempty
copytruncate
}
这样,不管一天轮换多少次,超过 30 天的日志都会被清理。
我的建议:生产环境,按时间轮换时,rotate 值设为“保留天数 + 1”。比如保留 7 天,就设 rotate 7。按大小轮换时,根据磁盘空间和单文件大小估算。比如磁盘有 10G 空间,单文件 100M,那 rotate 100 以内都安全。留点余量,别算太满。
4.5 综合实战:一个完整的轮换脚本
最后,咱们把上面这些知识点串起来,写一个生产级别的轮换配置。这个配置我用了好几年,在几十台服务器上跑过,没出过问题。
/var/log/webapp/*.log {
# 同时按大小和时间触发
daily
maxsize 200M
# 保留最近 30 份
rotate 30
# 压缩配置
compress
delaycompress
# 文件名用日期
dateext
dateformat -%Y%m%d
extension .log
# 安全选项
missingok
notifempty
copytruncate
# 轮换后执行
postrotate
# 给 Java 应用发信号,让它重新打开日志
kill -USR1 $(cat /var/run/webapp.pid 2>/dev/null) 2>/dev/null || true
endscript
}
这个配置做到了:
- 每天轮换一次,但如果某天日志量暴增超过 200M,也会触发轮换。
- 保留 30 份,也就是大约一个月的日志。
- 当天日志不压缩,方便排查;第二天自动压缩。
- 文件名带日期,一眼可读。
- 轮换后通知应用重新打开日志句柄。
最后提醒一句:配好轮换后,一定要手动跑一次 logrotate -d /etc/logrotate.d/yourconfig 做调试。 -d 是 debug 模式,只打印执行计划,不实际执行。我见过太多人配完直接上生产,结果轮换没生效,日志把磁盘撑爆了。别问我怎么知道的。