Nginx access_log 写入失败时静默丢弃日志,不重试、不告警、不降级;失败原因包括磁盘满、权限不足、SELinux拦截、句柄泄漏等,需通过缓冲配置、syslog队列、路径监控等主动加固。

当 Nginx 的 access_log 写入失败时,它本身不重试、不告警、不降级,而是静默丢弃日志——这是默认行为,也是多数线上问题难以被及时发现的根源。
写入失败时 Nginx 实际做了什么
Nginx worker 进程在尝试写日志时,若系统调用(如 write() 或 fsync())返回错误(例如 ENOSPC 磁盘满、EACCES 权限拒绝、EBADF 文件描述符失效),它会直接跳过本次日志记录,继续处理请求。不会记录错误到 error_log(除非是初始化阶段打开文件失败),也不会中断请求流程。
- 日志缓冲区(buffer)满后尝试刷盘失败 → 缓冲区清空但内容丢失,不报警
- 使用
syslog时 socket 发送失败 → 默认丢弃,除非配置了 syslog 守护进程的磁盘队列 - logrotate 后未发送
USR1信号 → worker 仍往已删除文件句柄写入 → 返回EBADF,静默失败
哪些情况会导致“写入失败”却不报错
这类问题常表现为 access_log 停更、但 error_log 也无相关提示,需主动排查:
使用tbot机器ID身份文件配合tsh CLI,通过Teleport访问控制SSH登录托管主机或执行远程命令。
-
磁盘 Inode 耗尽:
df -i显示 100%,无法创建新文件(尤其 logrotate 生成大量.log.1、.log.2.gz后未清理) -
目录权限缺失:Nginx 工作用户(如
www-data)对日志路径的任一父目录缺少x(执行/进入)权限,导致 open 失败 -
SELinux/AppArmor 拦截:安全模块阻止
nginx进程写日志目录,ausearch -m avc -ts recent | grep nginx可捕获拒绝事件 -
文件句柄泄漏:长期运行后大量 “deleted” 日志文件仍被 worker 占用,
lsof +L1 | grep nginx可查
增强容错的实用配置建议
靠默认机制无法应对生产环境不确定性,需主动加固:
- 启用带 flush 的 buffer:
access_log /var/log/nginx/access.log main buffer=32k flush=1s;—— 减少写频次,缓解瞬时 I/O 压力 - 改用 syslog 并开启磁盘队列:
access_log syslog:server=127.0.0.1:514,facility=local7 main;,并在 rsyslog 中配置$ActionQueueFileName fwdRule1和$ActionQueueMaxDiskSpace 1g - 为关键路径显式关闭日志:
location = /healthz { access_log off; },避免低价值请求挤占 I/O 资源 - 设置日志路径监控脚本:定期检查
stat /var/log/nginx/access.log修改时间 +ls -l /proc/$(cat /run/nginx.pid)/fd/ | grep access是否有效
验证写入是否真正生效的方法
不能只看文件是否存在或有无内容,要确认写入链路完整:
- 用
nginx -t && nginx -s reload后,立即执行curl -I http://localhost/,再tail -n1 /var/log/nginx/access.log查最新行 - 用
strace -p $(pgrep -f "nginx: worker") -e write,fsync 2>&1 | grep -E "(access|log)"观察实际系统调用结果 - 模拟磁盘满:
dd if=/dev/zero of=/var/log/nginx/fill bs=1M count=1000,观察是否出现静默丢日志,再清理后验证恢复逻辑

















