Nginx日志丢失本质是轮转抖动导致的写入中断,非网络丢包;需通过tcpdump/ss验证TCP状态,对比inode、检查lsof句柄、加固buffer+flush配置、确保USR1协同及采集端稳定。

这不是丢包,是日志丢失——Nginx 本身不发包,也不会在网络层“丢包”;所谓“丢包故障”,实际是高并发下日志轮转瞬间写入中断、文件句柄错位或采集脱节,导致请求记录缺失,被误判为网络丢包。
确认是不是真丢了日志,而不是网络层丢包
先排除干扰:用 tcpdump 或 ss -i 看真实 TCP 连接状态和重传情况。如果 Retransmit 为 0、rcv_space 充足、ESTABLISHED 连接稳定,但 access.log 里缺请求记录,那就不是网络丢包,而是日志链路断了。
快速验证方法:
- 对比同一时间窗口的原始日志与采集端(如 ES/ZincSearch)数据:
tail -n 100 /var/log/nginx/access.log和后端查询最近 100 条,看时间戳是否连续、请求数是否匹配 - 检查 inode 是否跳变:
ls -i /var/log/nginx/access.log*,若轮转后新文件 inode 与旧文件无关,但日志内容突然中断,说明 worker 进程没收到 USR1,还在往已删除的旧文件写(lsof 显示 deleted 状态)
重点查 logrotate 与 Nginx 的信号协同是否可靠
高并发下最常见问题是 logrotate 执行完 rename,却没成功通知 Nginx 切换句柄——尤其在主备切换、systemd 单元延迟或权限隔离场景中。
必须做到:
- logrotate 配置启用
sharedscripts和dateext,时间格式设为dateformat -%Y%m%d-%H%M%S,避免毫秒级同名覆盖 - postrotate 脚本内不调
systemctl reload nginx,改用轻量校验+USR1:
if [ -f /run/nginx.pid ] && kill -0 $(cat /run/nginx.pid) >/dev/null 2>&1; then
kill -USR1 $(cat /run/nginx.pid) 2>/dev/null || true
fi - 手动触发测试:
logrotate -f /etc/logrotate.d/nginx,再立刻执行lsof -p $(cat /run/nginx.pid) | grep access,确认 worker 打开的是新生成的当前日志文件,而非带deleted标记的旧文件
加固日志写入链路,扛住轮转抖动
reload 或 USR1 不该导致日志丢失——问题出在缓冲未刷、权限错位或路径不可写。
- 显式配置带 flush 的缓冲:
access_log /var/log/nginx/access.log main buffer=64k flush=3s;,避免高并发积压后进程退出时内存缓冲清空 - 确保日志目录属主与 worker 进程用户一致(如
nginx:adm),并提前验证:sudo -u nginx touch /var/log/nginx/test.log - 禁用 reload 自动 reopen 日志:所有日志切分只由 USR1 触发,不要依赖
nginx -s reload切换日志文件句柄 - 检查 SELinux 拦截:
sudo ausearch -m avc -ts recent | grep nginx,若有拒绝记录,加策略:sudo semanage fcontext -a -t httpd_log_t "/var/log/nginx(/.*)?"
检查采集端是否跟丢了轮转后的文件
Filebeat、Fluentd 等工具在高并发轮转时容易因 inode 变更、扫描延迟或 harvester 关闭过早漏采。
- 调大
close_inactive: 30s(默认 5s 太短,轮转后可能还没发现新文件) - 设
scan_frequency: 2s加快轮转感知,harvester_buffer_size: 65536提升小包读取稳定性 - 禁用
max_bytes(默认 10MB 易引发内存抖动),改用小块稳定读取 - 确认 registry 文件持久化且磁盘未满,传输链路走 TLS,避免中间丢包


















