logrotate默认不支持分钟级切割,因其daily、hourly为硬编码周期,底层依赖cron最小1分钟粒度,且自身无minutely选项;同时持续写入日志的mtime不变,导致频繁调用也难触发轮转。

为什么 logrotate 默认不支持分钟级切割
logrotate 的 daily、hourly 是硬编码周期,底层依赖系统 cron 的最小粒度(通常为 1 分钟),但 logrotate 自身没有「每分钟执行一次」的触发逻辑,强行配 hourly + minutely 不存在的选项会直接报错 unknown keyword 'minutely'。更关键的是,即使每分钟跑一次 logrotate,它默认按文件修改时间判断轮转时机,而 Nginx 日志文件在持续写入,mtime 不变,导致绝大多数调用实际不触发切割。
用 cron + mv + kill -USR1 组合实现可靠分钟切割
核心思路是绕过 logrotate,由 cron 每分钟发信号让 Nginx 自己切日志,再立即重命名旧文件。这利用了 Nginx 原生支持的 USR1 信号机制,比外部工具挪文件更安全(避免写入丢失)。
实操步骤:
- 确保 Nginx 配置中
access_log和error_log使用绝对路径,例如/var/log/nginx/access.log - 在 crontab 中添加:
* * * * * /bin/bash -c 'mv /var/log/nginx/access.log /var/log/nginx/access_$(date +\%Y\%m\%d_\%H\%M).log 2>/dev/null || true; kill -USR1 $(cat /var/run/nginx.pid 2>/dev/null) 2>/dev/null || true' - 注意
mv必须在kill -USR1前执行,否则新日志会继续写进原文件;|| true防止某次失败中断后续执行 - 第一次运行前手动 touch 一下
/var/log/nginx/access.log,确保文件存在,避免 mv 报错
切割后日志文件名带毫秒或序号怎么避免重复
单纯用 date +\%H\%M 在高并发场景下,同一分钟内多次触发(如 cron 延迟叠加、脚本卡顿)会导致覆盖。解决方案是加随机后缀或用原子操作:
- 改用
$(date +\%Y\%m\%d_\%H\%M)_$$,$$是当前 shell 进程 PID,保证单次执行唯一 - 更稳妥的做法:先
mv到临时名(如access.log.tmp),再用date生成目标名并mv,两次操作间极短,基本无冲突 - 不要用
%S(秒),它会让文件按秒切,不是需求中的「按分钟」
systemd 环境下 kill -USR1 失败的常见原因
在 CentOS 7+/Ubuntu 16.04+ 等 systemd 系统中,直接读 /var/run/nginx.pid 可能失败,因为 pid 文件可能被放在 /run/nginx.pid,或者 systemd 启动的 Nginx 根本不写 pid 文件(由 systemd 自己管理进程)。这时 kill -USR1 会报 No such process。
解决办法:
- 查真实主进程 PID:
systemctl show --property MainPID nginx | cut -d'=' -f2 - 或直接向服务发信号:
systemctl kill --signal=USR1 nginx - 验证是否生效:切完后检查
/var/log/nginx/access.log是否变成空文件,且有新命名的归档文件生成
Nginx 按分钟切日志本质是「定时信号 + 原子重命名」,重点不在工具选型,而在顺序和容错——mv 和 kill -USR1 的先后不能反,cron 的每分钟调度必须稳定,systemd 下的进程定位不能依赖传统 pid 文件。


















