如何通过Nginx内置变量 $request_completion 准确判断客户端请求是否完整记录到日志?
- 内容介绍
- 文章标签
- 相关推荐
本文共计993个文字,预计阅读时间需要4分钟。
Nginx 的 `$request_completion` 变量用于标识当前请求是否已被完整处理(即客户端端已正常接收完所有数据,Nginx 也已完成响应)。在日志中,它有助于识别因网络中断、客户端提前关闭连接、超时或主动取消导致的请求不完整问题。这对于排查前端加载失败、API调用异常、埋点丢失等问题非常实用。
理解 $request_completion 的取值逻辑
该变量仅在请求结束时(log 阶段)可用,取值为:
- "OK":请求完整处理完成,包括客户端发完所有请求体(如 POST 数据)、服务端返回完整响应、TCP 连接正常关闭;
-
空字符串(""):请求未完成,常见于客户端断连、读取请求体超时(
client_body_timeout)、发送响应中途断开、或 worker 进程被强制终止等场景。
注意:$request_completion 不反映 HTTP 状态码是否为 2xx,即使返回 500,只要流程走完、响应发出,它仍为 "OK";反之,即使返回 200,若客户端在响应写入一半时关闭连接,它就是空值。
在 access_log 中添加 completion 标记
在 http、server 或 location 块中定义带该变量的日志格式:
log_format detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_completion';
然后启用日志:
access_log /var/log/nginx/access.log detailed;
日志样例:
192.168.1.100 - - [10/Jul/2024:14:22:35 +0800] "POST /api/submit HTTP/1.1" 200 42 "-" "curl/8.6.0" OK-
192.168.1.101 - - [10/Jul/2024:14:22:37 +0800] "POST /api/submit HTTP/1.1" 200 0 "-" "Mozilla/5.0..." ""(空值表示响应未发完或请求体未收全)
结合其他变量做有效过滤与分析
单独看 $request_completion 价值有限,建议组合关键字段定位问题:
- 筛选不完整请求:
awk '$12 == "" {print}' /var/log/nginx/access.log(假设第12列为$request_completion); - 重点关注大请求体未完成的情况:
grep '""' access.log | awk '$10 == 0 && $9 > 10240 {print}'(响应字节数为 0 且请求体超 10KB,大概率是客户端中断上传); - 关联
$request_time和$upstream_response_time:若$request_time显著大于后者,且$request_completion为空,说明响应阶段卡住或客户端断连; - 配合
$connection_requests和$server_protocol判断是否集中出现在 HTTP/1.1 长连接复用末期或 TLS 握手异常后。
注意事项与常见误判场景
该变量无法捕获所有“逻辑不完整”行为,需避免误解:
- 客户端发起请求后立即关闭(如 JS 中
fetch().catch()后未 await,或 Axios timeout 触发 abort),Nginx 通常记录为空,但不是所有浏览器都严格遵循 RST 行为; - 使用
proxy_buffering off或流式响应(如 SSE、chunked transfer)时,$request_completion仍只反映连接终态,不表示业务逻辑执行完毕; - 在
if或重写逻辑中提前return,只要响应已发出,仍为 "OK";只有未进入输出阶段就终止(如读取 body 超时)才为空; - 该变量在
log_by_lua*中不可用(Lua 阶段早于 log 阶段),如需 Lua 处理,请改用ngx.ctx在header_filter_by_lua*或body_filter_by_lua*中标记,再透传至日志变量。
本文共计993个文字,预计阅读时间需要4分钟。
Nginx 的 `$request_completion` 变量用于标识当前请求是否已被完整处理(即客户端端已正常接收完所有数据,Nginx 也已完成响应)。在日志中,它有助于识别因网络中断、客户端提前关闭连接、超时或主动取消导致的请求不完整问题。这对于排查前端加载失败、API调用异常、埋点丢失等问题非常实用。
理解 $request_completion 的取值逻辑
该变量仅在请求结束时(log 阶段)可用,取值为:
- "OK":请求完整处理完成,包括客户端发完所有请求体(如 POST 数据)、服务端返回完整响应、TCP 连接正常关闭;
-
空字符串(""):请求未完成,常见于客户端断连、读取请求体超时(
client_body_timeout)、发送响应中途断开、或 worker 进程被强制终止等场景。
注意:$request_completion 不反映 HTTP 状态码是否为 2xx,即使返回 500,只要流程走完、响应发出,它仍为 "OK";反之,即使返回 200,若客户端在响应写入一半时关闭连接,它就是空值。
在 access_log 中添加 completion 标记
在 http、server 或 location 块中定义带该变量的日志格式:
log_format detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_completion';
然后启用日志:
access_log /var/log/nginx/access.log detailed;
日志样例:
192.168.1.100 - - [10/Jul/2024:14:22:35 +0800] "POST /api/submit HTTP/1.1" 200 42 "-" "curl/8.6.0" OK-
192.168.1.101 - - [10/Jul/2024:14:22:37 +0800] "POST /api/submit HTTP/1.1" 200 0 "-" "Mozilla/5.0..." ""(空值表示响应未发完或请求体未收全)
结合其他变量做有效过滤与分析
单独看 $request_completion 价值有限,建议组合关键字段定位问题:
- 筛选不完整请求:
awk '$12 == "" {print}' /var/log/nginx/access.log(假设第12列为$request_completion); - 重点关注大请求体未完成的情况:
grep '""' access.log | awk '$10 == 0 && $9 > 10240 {print}'(响应字节数为 0 且请求体超 10KB,大概率是客户端中断上传); - 关联
$request_time和$upstream_response_time:若$request_time显著大于后者,且$request_completion为空,说明响应阶段卡住或客户端断连; - 配合
$connection_requests和$server_protocol判断是否集中出现在 HTTP/1.1 长连接复用末期或 TLS 握手异常后。
注意事项与常见误判场景
该变量无法捕获所有“逻辑不完整”行为,需避免误解:
- 客户端发起请求后立即关闭(如 JS 中
fetch().catch()后未 await,或 Axios timeout 触发 abort),Nginx 通常记录为空,但不是所有浏览器都严格遵循 RST 行为; - 使用
proxy_buffering off或流式响应(如 SSE、chunked transfer)时,$request_completion仍只反映连接终态,不表示业务逻辑执行完毕; - 在
if或重写逻辑中提前return,只要响应已发出,仍为 "OK";只有未进入输出阶段就终止(如读取 body 超时)才为空; - 该变量在
log_by_lua*中不可用(Lua 阶段早于 log 阶段),如需 Lua 处理,请改用ngx.ctx在header_filter_by_lua*或body_filter_by_lua*中标记,再透传至日志变量。

