一、基本原理
logrotate 由 cron/timer 每天触发,按配置把当前日志改名归档、创建新文件、删除超期归档,并可选地通知服务重新打开日志文件。
配置文件在 /etc/logrotate.conf(全局)与 /etc/logrotate.d/(各服务)。
二、一个完整配置
/var/log/myapp/*.log {
daily
rotate 7
size 100M
compress
delaycompress
missingok
notifempty
dateext
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}
逐项说明:
| 指令 | 作用 |
|---|---|
daily / weekly / monthly | 切割周期 |
rotate 7 | 保留 7 份,多的删掉 |
size 100M | 超过 100M 也切(与 daily 同时写时,满足其一就切) |
compress | 归档用 gzip 压缩 |
delaycompress | 延后一轮再压缩,避免刚写完的文件被压 |
missingok | 文件不存在不报错 |
notifempty | 空文件不切 |
dateext | 用日期做后缀,而不是 .1 .2 |
create 0640 user group | 切完新建文件并设权限属主 |
sharedscripts | 多个文件只执行一次脚本 |
三、最关键的 postrotate
日志文件被改名后,正在写它的进程仍然持有旧文件的句柄,会继续往 myapp.log.1 里写,新文件一直是空的,磁盘也不会释放。所以必须让进程重新打开日志:
postrotate
systemctl reload myapp
endscript
或者给进程发信号(Nginx 是 kill -USR1):
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
四、copytruncate 的取舍
有些程序不支持重开日志,可以用:
copytruncate
它先复制文件再清空原文件,进程句柄不变。缺点是复制与清空之间写入的日志会丢,高写入量场景不推荐。
五、手动测试
# 预演,只显示会做什么,不实际执行
logrotate -d /etc/logrotate.d/myapp
# 强制执行一次(忽略周期限制)
logrotate -f /etc/logrotate.d/myapp
# 看状态记录
cat /var/lib/logrotate/status
改完配置一定先 -d 预演,看清楚它会动哪些文件。
六、几个常见坑
- 配置没生效:logrotate 由
/etc/cron.daily/logrotate或logrotate.timer触发,容器里常常没有 cron,需要自己加定时任务。 - 权限:
create的属主写错,服务重启后写不了日志。 - 通配符匹配到不该切的文件:
/var/log/myapp/*.log如果包含正在被 tail 的调试文件,会让人以为日志丢了。 - 压缩后仍占空间:如果进程还持有已删除文件的句柄,空间不会释放,
lsof | grep deleted能看到,重启或重开日志才释放。 - 归档目录:默认放在同目录,日志多时建议单独挂盘或用
olddir指到别处。
七、容器环境的做法
容器里通常没有 cron,常见方案:
- 用 sidecar 容器专门跑 logrotate。
- 应用自己按大小切分(如 log4j 的
RollingFileAppender、Python 的RotatingFileHandler)。 - 日志直接输出到 stdout,交给 Docker/ELK 收集,不在容器内落盘——这也是最推荐的方式。
八、配套监控
切割只是控制增长,不是解决增长。建议同时做:
- 磁盘使用率告警(80% 预警)。
- 日志量突增告警(往往是错误循环刷屏)。
- 归档保留期与合规要求对齐(有些行业要求保留 180 天)。
小结
daily + rotate 7 + compress + postrotate reload 是标准配置;切完必须让进程重开日志,否则旧文件继续变大;容器里没有 cron,优先把日志交给 stdout 收集。