logrotate 不会自动管理自定义应用日志,必须显式配置路径、启用定时任务、验证执行状态并正确编写 postrotate 脚本。

logrotate可不是那种装完就自动替你管理应用日志的工具。像 /var/log/myapp/app.log 这样的路径,必须明确地写进配置里,否则它就只会去处理 /var/log/syslog 这些系统日志。在磁盘被撑爆之前,你压根儿不会意识到它对你自己的服务完全“视若无睹”。
怎么确认 logrotate 真正在跑
别信“预装=启用”。很多容器或最小化安装环境压根没激活 cron 调度:
- 运行
sudo systemctl list-timers | grep logrotate,有输出才说明 systemd timer 已启用;若无,检查/etc/cron.daily/logrotate是否存在且可执行 - 手动触发一次模拟运行:
sudo logrotate -d /etc/logrotate.conf,重点看输出里有没有error(比如file not found、permission denied) - 查状态文件:
cat /var/lib/logrotate/status,最后一行时间戳是否在最近24小时内?不是,说明 cron 没跑成
为自定义日志写独立配置文件
把规则塞进 /etc/logrotate.d/ 是最安全、最易维护的做法,全局配置 /etc/logrotate.conf 少动为妙:
- 新建文件:
sudo nano /etc/logrotate.d/myapp - 内容必须包含日志路径、大括号块、至少一个触发条件(如
daily或size 100M)和rotate数值 - 关键参数要对齐实际需求:高写入服务(如 Nginx)建议加
copytruncate,避免依赖服务 reload;低频服务可用create 0644 myuser mygroup - 务必用绝对路径写
postrotate里的命令,例如/bin/kill -USR1 `cat /var/run/myapp.pid`,不能写kill
为什么 postrotate 经常失效
postrotate 不是“执行完就完事”,它失败时 logrotate 默认不报错,但旧日志句柄不会释放,导致新日志继续写进 .1 文件——这是日志“看似轮转了,实则还在涨”的元凶:
- 加
sharedscripts,否则每个匹配到的日志文件都会单独执行一遍postrotate,容易重复发信号 - 用
|| true收尾(如/bin/kill -USR1 `cat /var/run/myapp.pid 2>/dev/null` || true),防止某次 pid 文件暂缺导致整个轮转中断 - 不要依赖
systemctl reload:某些服务(如早期 rsyslog)reload 会丢日志,USR1信号才是标准做法
调试时最容易忽略的三件事
配置写完不测试 = 白写。真正上线前必须做这三步,缺一不可:
- 语法检查:
sudo logrotate -d /etc/logrotate.d/myapp,重点看 “error” 或 “warning” 行,尤其是file not found和permission denied - 强制执行一次:
sudo logrotate -vf /etc/logrotate.d/myapp,-v显示动作,-f强制触发,立刻看到归档文件生成 - 验证句柄是否更新:
lsof -p $(cat /var/run/myapp.pid) | grep log,确认进程打开的是新日志文件,而不是还挂在app.log.1上
最常被跳过的其实是最后一步:即使 .log.1 出来了,lsof 不验证,就无法确认服务是否真的切换到了新文件——旧句柄残留,等于白切。