Linux 日志轮转实战:logrotate 配置、按天切割与常见排错


一、为什么要做日志轮转

VPS 上运行的 Nginx、MySQL、PHP-FPM、系统 journal 等每天都在写入日志,如果不加控制,/var/log 目录会被撑爆,轻则磁盘告警,重则服务写入失败直接宕机。日志轮转(log rotation)就是按时间或体积把日志"切一刀":保留最近若干份,旧的自动压缩归档甚至删除,让磁盘占用始终维持在可控范围。

以日志体量最大的 Nginx 为例,单机日访问量 10 万 PV 时,access.log 一天就能增长 300MB~1GB。不做轮转,一块 40G 的系统盘半个月就会被塞满。本文从 Linux 自带的 logrotate 讲起,覆盖配置、手动调试与常见排错。

二、logrotate 的核心机制

logrotate 本身是一个由 cron 每天定时触发的程序,主配置在 /etc/logrotate.conf,各服务的独立配置放在 /etc/logrotate.d/ 目录下(debian/ubuntu 系)。每次运行时它会读取配置,判断日志文件是否满足"轮转条件",满足就把当前日志改名,并(可选)让服务重新打开日志句柄。

先看一下系统默认主配置:

# /etc/logrotate.conf
weekly                  # 默认每周轮转一次
rotate 4                # 默认保留 4 份历史日志
create                  # 轮转后创建新日志文件,保持原属主权限
dateext                 # 使用日期作为轮转后缀,而非 .1 .2 .3
compress                # 轮转后的旧日志使用 gzip 压缩
include /etc/logrotate.d # 载入服务级配置目录

最关键的一句话是 include /etc/logrotate.d,日常要新增自定义轮转规则,都是在这个目录下放一个独立配置文件,而不是改主配置。

三、编写自己的轮转规则

下面针对最常见的 Nginx 场景写一个配置 /etc/logrotate.d/nginx:

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily                 # 按天轮转
    rotate 14             # 保留 14 天
    maxsize 500M          # 体积超过 500M 也强制轮转(更稳妥)
    missingok             # 日志文件不存在时不报错
    notifempty            # 空日志文件不轮转
    compress              # 旧日志 gzip 压缩
    delaycompress         # 压缩延迟一天,方便当天排错
    dateext
    dateformat -%Y%m%d
    create 644 www-data www-data
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
    endscript
}

要点解释:

  • daily + maxsize 500M:两种触发条件"或"关系,先到先轮转,避免大流量站点当天日志过大。
  • postrotate:日志改名后,Nginx 还握着旧文件句柄继续写,必须发送 USR1 信号让 Nginx 重新打开新的 access.log,否则日志会一直写进已经改名(甚至被压缩)的旧文件里。
  • delaycompress:压缩晚一天执行,防止误删刚轮转出来还没排查完的日志。

MySQL 的慢查询日志同理,轮转后需要重新刷新日志句柄:

# /etc/logrotate.d/mysql
/var/log/mysql/slow-query.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    create 640 mysql mysql
    postrotate
        mysqladmin -uroot -pXXX flush-logs 2>/dev/null || true
    endscript
}

四、手动触发与调试

新配置不要等 cron 跑,手动验证:

# 模拟执行,只看会做什么,不真正轮转
logrotate -d /etc/logrotate.d/nginx

# 强制执行(忽略 daily/monthly 等时间条件)
logrotate -f /etc/logrotate.d/nginx

# 查看某份日志是否真的转成功了
ls -lh /var/log/nginx/

执行后的目录效果:

-rw-r--r-- 1 www-data www-data  180M  2026-10-09 10:00 access.log
-rw-r--r-- 1 www-data www-data  512M  2026-10-08 10:00 access.log-20261008
-rw-r--r-- 1 www-data www-data  480M  2026-10-07 10:00 access.log-20261007.gz

-d 是 dry-run,输出里 considering log /var/log/nginx/access.log 后如果出现 log needs rotating 说明条件已满足;出现 log does not need rotating 说明还没到时间点,这是排查"为什么没轮转"的第一招。

五、常见排错对照表

现象原因解决办法
cron 每天跑但日志没被轮转配置在非 root 用户目录、或文件属主无权限确保配置在 /etc/logrotate.d/,用 logrotate -d 查看权限报错,检查 create 的属主设置
轮转后日志内容丢失/继续写旧文件postrotate 的 USR1 信号没发出去确认 pid 文件路径,手动执行 kill -USR1 $(cat /var/run/nginx.pid) 验证
报错 error: skipping ... because parent directory has insecure permissions日志目录权限过宽(如 777)把目录权限收紧到 755,或配置加 su root adm 指定运行身份
轮转文件没有按日期命名漏写 dateext / dateformat补上 dateext 并加 dateformat -%Y%m%d
轮转时服务报 "No such file"日志文件刚被误删,服务重建失败加 create 指令并配置正确属主;对 Nginx 再加 kill -USR1

另外注意:Ubuntu/Debian 的 logrotate 由 /etc/cron.daily/logrotate 触发,执行时间是每天 6:25 左右;如果你的应用对日志实时性要求高,可以自行加一个 crontab 更频繁触发:

# crontab -e
*/30 * * * * /usr/sbin/logrotate -f /etc/logrotate.d/nginx >/dev/null 2>&1

六、systemd journal 的单独治理

现在很多系统日志走 journald,它也有自己的轮转与上限配置(/etc/systemd/journald.conf):

# /etc/systemd/journald.conf
SystemMaxUse=200M     # journal 最多占用 200M
SystemMaxFileSize=50M # 单份日志文件上限
MaxRetentionSec=7d    # 保留 7 天
RuntimeMaxUse=50M     # /run 内存中日志上限
# 修改后生效
systemctl restart systemd-journald
# 立即强制清理
journalctl --vacuum-size=100M
journalctl --vacuum-time=3d

七、总结

一套稳妥的日志轮转方案 = logrotate 按天/体积切割 + 保留合理份数 + 压缩归档 + postrotate 重开句柄 + 定期 dry-run 验证。配置完成后建议观察一周:确认 access.log 的日期后缀文件正常生成、压缩正常执行、服务无报错,再放心交给 cron。真正把磁盘告警消灭在日志层面,是 VPS 运维最值得投入的十分钟。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

VPS网络加速实战:TCP BBR开启、队列算法与内核参数调优

VPS磁盘扩容实战:控制台扩容后growpart扩展分区与文件系统resize全流程

评 论
更换验证码