http/2在nginx中按连接粒度聚合日志,非每请求一条;$request常为空,因不解析二进制帧;access_log缓冲易丢末尾日志;$connection_requests为累计值,不反映实时并发流数。

HTTP/2 连接在 Nginx 中的日志行为和 HTTP/1.x 完全不同——不是格式写错,而是底层记录逻辑变了。很多团队发现 access_log 里时间戳不准、连接数对不上、甚至某些请求“消失”,根源往往就在这里。
HTTP/2 日志不记录单个请求,而是按连接粒度聚合
Nginx 原生 http 模块(非 nghttp2)对 HTTP/2 的访问日志默认以 整个 TCP 连接 为单位记录,而不是每个请求一条日志。也就是说,一次 HTTP/2 多路复用连接中发起的 10 个请求,可能只在 access_log 里留下 1 条记录(连接建立或关闭时),其余请求不会触发新日志行。
- 这是设计使然,不是 bug:Nginx 把 $request、$status、$bytes_sent 等变量绑定在 connection 上,而非 stream 上
- 若你依赖 access_log 统计 QPS 或分析单请求耗时,这种聚合会严重失真
- 验证方式:用 curl -k --http2 https://your.site/api/test 发起多次请求,观察日志行数是否远少于实际请求数
$request 变量在 HTTP/2 下始终为空或显示“-”
当启用 http_v2 模块后,标准日志变量 $request 在大多数情况下无法正确提取请求行(如 GET /api/user HTTP/2),常输出为 - 或空字符串。
- 原因:HTTP/2 不再以明文文本帧传输请求行,而是用二进制 HPACK 编码,Nginx 默认不解析还原
- 替代方案:改用 $uri + $args 组合近似还原路径,但无法获取 method 和 HTTP 版本
- 更可靠的做法是启用 debug 日志级别 并配合 error_log 查看详细帧信息(需编译时含 --with-debug)
access_log 缓冲机制在 HTTP/2 下更容易丢失末尾日志
HTTP/2 连接生命周期长、空闲时间久,而 Nginx 的 access_log buffer(如 buffer=64k flush=10s)依赖活跃写入或超时刷盘。若连接长时间无新流、最后仅以 GOAWAY 结束,buffer 中未满的数据可能被直接丢弃,导致该连接的最终日志缺失。
- 典型现象:压测结束后查日志,发现最后一批请求没记录,但 error_log 显示连接已正常关闭
- 缓解方法:在 http 块中显式降低 flush 阈值,例如 access_log /var/log/nginx/access.log main buffer=8k flush=3s;
- 生产环境不建议完全禁用 buffer(即去掉 buffer 参数),会显著增加磁盘 I/O 压力
不要依赖 $connection_requests 统计并发流数
$connection_requests 表示当前连接处理过的请求数(累计值),它在 HTTP/2 下仍递增,但不能反映实时并发流数(concurrent streams)。一个连接上可能同时存在 5 个正在传输的流,但 $connection_requests 仍是 100(历史总数)。
- 想监控真实并发流压力?需依赖 nginx stub_status 模块 + 第三方 exporter(如 nginx-vts-exporter),或开启 ngx_http_v2_module 的 debug 日志查看 SETTINGS/HEADERS 帧
- 注意:stub_status 默认不暴露流级指标,需 patch 或用支持 v2 的增强版模块











