logrotate 的 dateext 不支持自动创建日期子目录,需用脚本配合 mkdir -p 和 mv 实现路径准备,并在 postrotate 中调用,再发 usr1 信号重载 nginx 日志。

为什么不能直接用 logrotate 配合 dateext?
因为 logrotate 的 dateext 默认只在文件名末尾加日期(如 access.log-20241015),不会自动创建按日期命名的子目录(如 2024/10/15/access.log)。硬套用会导致日志写入失败或路径不存在,Nginx 会因无法打开日志文件而静默丢弃日志。
正确做法:用 shell 脚本 + mv + mkdir -p 构建日期路径
核心是把日志先切到临时位置,再按 $(date +%Y/%m/%d) 拆解并移动进嵌套目录。关键点不是“分割”,而是“重定向写入前的路径准备”。
示例脚本逻辑:
#!/bin/bash LOG_DIR="/var/log/nginx" DATE_DIR=$(date +%Y/%m/%d) FULL_PATH="$LOG_DIR/$DATE_DIR" mkdir -p "$FULL_PATH" mv "$LOG_DIR/access.log" "$FULL_PATH/access.log" mv "$LOG_DIR/error.log" "$FULL_PATH/error.log" kill -USR1 $(cat /var/run/nginx.pid)
注意:kill -USR1 是 Nginx 重新打开日志文件的信号,必须在移动后立即触发,否则新日志仍写入旧路径。
-
mkdir -p必须带-p,否则遇到2024/10目录不存在时会失败 - 脚本需在
logrotate的postrotate段里调用,不能替代logrotate主流程 - 确保 Nginx 进程对目标目录有写权限,比如
chown -R www-data:www-data /var/log/nginx
如果想让 Nginx 直接写入日期目录,得改 access_log 指令
Nginx 本身不支持动态日期路径,但可以配合 perl_set 或 Lua 模块实现。更稳妥的是用 open_log_file_cache off + 定时 reload,不过代价是每次 reload 会短暂中断连接。
纯静态配置下唯一可行的折中方案是:在 nginx.conf 中把日志路径写成固定层级,例如:
access_log /var/log/nginx/year/month/day/access.log;
但这只是占位符,真实路径仍需靠外部脚本在每天零点前创建好对应目录,并在 reload 前确保路径存在 —— 实际上又绕回上一个方案。
所以真正落地时,几乎所有人都选择「logrotate 切文件 → 自定义脚本挪进日期目录 → 发 USR1」这个链路。
容易被忽略的权限和 SELinux 问题
常见现象是脚本手动执行成功,但 logrotate 自动跑就失败,日志里只显示 open() "/var/log/nginx/2024/10/15/access.log" failed (13: Permission denied)。
- 检查
logrotate运行身份(通常是root),但目标目录可能被nginx进程以www-data用户创建,导致属主冲突 - SELinux 启用时,
logrotate默认不允许写入非标准日志路径,需运行semanage fcontext -a -t var_log_t "/var/log/nginx(/.*)?"并restorecon -Rv /var/log/nginx -
mv操作跨文件系统会失败,确保源日志和目标日期目录在同一挂载点(比如都位于/var/log分区)
路径越深、权限越细、环境越锁死,越容易卡在这一步。别只盯着脚本逻辑,先 ls -Z 和 id -u $(ps -o user= -p $(cat /var/run/nginx.pid)) 看清楚实际上下文。











