Nginx 不提供完整 HTTP 生命周期多阶段打点,但可通过 $msec、$request_time、$upstream_connect_time、$upstream_header_time、$upstream_response_time 和 $ssl_handshake_time 等内置变量组合,在日志中还原关键时间节点;需按请求类型(静态/代理/FastCGI)区分使用,并借助差值估算上传耗时、Nginx 处理开销及 TLS 占比。

Nginx 本身不提供完整 HTTP 生命周期的多阶段打点(如 DNS 解析、TCP 建立、TLS 握手、首字节返回等),但可通过组合多个内置变量,结合日志格式定制与外部工具,还原单次请求在 Nginx 视角下的关键时间节点。重点不是“全链路追踪”,而是抓住 Nginx 实际可观测的、有明确语义的耗时切片。
记录核心时间变量,覆盖请求生命周期主干
Nginx 在处理一个请求过程中,会按顺序触发多个内部阶段,每个阶段都有对应变量可捕获时间戳或耗时:
-
$msec:日志写入时刻的绝对时间(秒 + 毫秒),精度高,是所有时间分析的锚点; -
$request_time:从接收到请求第一个字节开始,到响应最后一个字节发送完毕为止的总耗时(单位:秒,含毫秒); -
$upstream_connect_time:仅代理场景下有效,表示 Nginx 与后端建立 TCP 连接所用时间(含 DNS 查询、TCP 握手、TLS 协商); -
$upstream_header_time:从向后端发起请求,到收到响应头第一个字节的时间(即“首包延迟”); -
$upstream_response_time:从发起请求到收到完整响应体的时间(含 header + body),常用于后端性能归因; -
$ssl_handshake_time(需启用ngx_http_ssl_module):TLS 握手耗时(单位:秒),仅 HTTPS 请求有效。
这些变量不是并列关系,而是存在嵌套与覆盖逻辑。例如:
-
$upstream_header_time≤$upstream_response_time≤$request_time -
$upstream_connect_time是$upstream_header_time的前置子集(若连接复用则为 0)
建议在 log_format 中统一记录:
log_format trace_log '$remote_addr - [$time_local] "$request" '
'$status $body_bytes_sent '
'$request_time $upstream_connect_time '
'$upstream_header_time $upstream_response_time '
'$ssl_handshake_time $msec';区分请求类型,避免变量失效或误导
不同请求路径适用的变量组合不同,混用会导致字段为空或含义错乱:
- 静态资源(root/alias 直接服务):
$upstream_*全为空或-,应聚焦$request_time+$msec+$sent_http_content_length(判断是否命中缓存); - 代理请求(proxypass):`$upstream*
才有意义,必须配合$upstream_addr` 使用,否则无法定位具体后端节点; - FastCGI / gRPC 等协议:对应变量为
$fastcgi_upstream_*或$grpc_upstream_*,命名规则一致但前缀不同; - 启用
keepalive的上游连接:$upstream_connect_time多数为0.000,不代表网络快,而是复用了已有连接。
示例 location 分离配置:
location /api/ {
proxy_pass http://backend;
access_log /var/log/nginx/api_trace.log trace_log;
}
location ~* \.(js|css|png|jpg)$ {
expires 1h;
add_header X-Cache-Status $upstream_cache_status; # 注意:静态服务下此值仍有效
access_log /var/log/nginx/static_trace.log trace_log;
}用时间差反推关键阶段耗时
Nginx 不直接暴露“客户端上传时间”或“Nginx 内部处理时间”,但可通过变量差值近似估算:
- 客户端上传耗时 ≈
$request_time−$upstream_response_time(仅代理场景;若为静态,则 ≈$request_time− 文件读取+发送开销,后者通常 <10ms) - Nginx 自身处理开销(不含 I/O)≈
$upstream_header_time−$upstream_connect_time(即后端响应头生成+网络传输,排除连接建立) - TLS 握手占比 ≈
$ssl_handshake_time/$request_time(若 >0.3,需检查证书链、OCSP Stapling 配置)
注意:所有差值计算应在日志采集后由 Loki/Prometheus 等工具完成,不要在 Nginx 配置中用 set 计算——变量在日志写入时已固化,不可动态运算。
辅助验证:通过响应头暴露实时时间信息
对调试或临时排查,可在响应头中直接透出关键耗时,无需解析日志:
add_header X-Req-Time $request_time; add_header X-Upstream-Time $upstream_response_time; add_header X-TLS-Time $ssl_handshake_time; add_header X-Msec $msec;
curl 测试即可快速确认:
curl -I https://example.com/api/v1/user | grep "X-" # 输出类似: # X-Req-Time: 0.082 # X-Upstream-Time: 0.075 # X-TLS-Time: 0.012 # X-Msec: 1720389642.123
这样能绕过日志延迟,实现秒级反馈。
不复杂但容易忽略


















