日志不丢的关键是让日志系统扛住 reload 抖动:强制缓冲 flush、禁用 reload 触发 reopen、协同 logrotate 与 usr1、校验路径权限、必要时改用 syslog。

平滑重载(nginx -s reload)本身不中断连接,但日志丢失常在此时集中暴露——问题不在 reload 动作本身,而在于日志写入链路在配置切换瞬间出现缓冲未刷、句柄未更新或权限错位。关键不是“怎么避免 reload”,而是让日志系统能扛住 reload 这一下抖动。
确保 reload 前日志缓冲已强制落盘
默认 access_log 的 buffer=8k 在高并发下极易积压,reload 时旧 worker 进程退出,未 flush 的缓冲区内容直接丢弃,且 Nginx 不报错、不重试。
- 显式配置带 flush 的缓冲:
access_log /var/log/nginx/access.log main buffer=64k flush=1s; - 禁用 reload 触发日志 reopen:所有日志轮转必须由
logrotate发送USR1完成,不要依赖nginx -s reload自动切换文件句柄 - 验证是否生效:执行
lsof -p $(cat /run/nginx.pid) | grep access,确认 worker 打开的是当前 active 日志文件,而非已删除的旧文件(状态含deleted)
统一日志轮转与重载信号的协同机制
logrotate 的 postrotate 脚本若直接调用 systemctl reload nginx,可能因 unit 状态延迟或权限隔离导致重载失败,进而跳过 USR1 通知,造成新日志写入旧文件句柄后被截断。
- 在
/etc/logrotate.d/nginx中使用轻量可靠方式:postrotate<br> if [ -f /run/nginx.pid ] && kill -0 `cat /run/nginx.pid` >/dev/null 2>&1; then<br> nginx -s reload >/dev/null 2>&1 || true<br> fi<br>endscript
- 确保
logrotate配置启用sharedscripts和dateext,避免主备节点同名覆盖 - 测试流程:手动触发
logrotate -f /etc/logrotate.d/nginx,再检查tail -f /var/log/nginx/access.log是否持续追加,且无中断
绕过 reload 对日志路径的依赖
如果 error_log 或 access_log 路径配置错误(如目录不存在、权限不足、SELinux 拦截),Nginx 会在 reload 时静默拒绝加载新配置,看似成功实则仍用旧配置——此时新日志规则不生效,旧日志持续写入,容易误判为“丢失”。
- reload 前必校验路径有效性:
sudo -u www-data mkdir -p /var/log/nginx && sudo -u www-data touch /var/log/nginx/test.log - 检查 SELinux 状态:
sudo ausearch -m avc -ts recent | grep nginx,若有拦截记录,用semanage fcontext -a -t httpd_log_t "/var/log/nginx(/.*)?"放行 - 临时改用 syslog 输出:
access_log syslog:server=127.0.0.1:514,facility=local7 main;,由 rsyslog 统一管理落盘与队列,脱离 Nginx 文件句柄生命周期
日志不丢,靠的不是 reload 多“平滑”,而是把写入、轮转、通知三环拆开盯紧。每一步都可验证,每一处失败都有迹可循。











