要精准定位建连瓶颈,必须在log_format中显式声明$upstream_connect_time,并结合$upstream_addr、$upstream_status及$upstream_cache_status(如MISS)进行归因分析,避免误判0.000值或忽略TLS握手时间。

要捕获 upstream_connect_time 并真正用于定位建连瓶颈,关键不是“记录下来”,而是让这个值可解析、可对比、可归因。它本身只是个浮点秒数(如 0.003),单独存在毫无意义。
必须在 log_format 中显式声明
Nginx 不会默认输出 upstream_connect_time。即使你用了 proxy_pass,只要 log_format 里没写 $upstream_connect_time,日志里就完全看不到它——不是为零,是压根不出现。
推荐定义一个清晰、字段对齐的格式:
log_format upstream_trace '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '$upstream_addr $upstream_status ' '$upstream_connect_time $upstream_header_time $upstream_response_time ' '$request_time "$http_user_agent"';
注意三点:
- 字段顺序固定,方便用
awk或日志采集器按空格/引号切分 -
$upstream_addr和$upstream_status必须紧邻时间字段,才能把耗时和具体后端 IP、状态码关联起来 - 不要加
int()或round()在 log_format 里做转换(除非你用的是 Nginx ≥ 1.19.7 且明确需要整数毫秒),原样保留浮点值(如0.027)更利于后续统计 P95/P99
只在真正走 upstream 的 location 中启用
$upstream_connect_time 仅在执行 proxy_pass、fastcgi_pass 等指令时有效。如果请求被 return、rewrite 或静态 root 拦截,该变量值为空或 -,日志中会显示短横线,干扰分析。
正确做法是:
location /api/ {
proxy_pass http://backend;
access_log /var/log/nginx/api_upstream.log upstream_trace;
}
location /static/ {
# 这里不配 access_log 或换用其他 format,避免混入无效 "-"
}
结合缓存状态过滤真实回源连接
如果你用 proxy_cache,很多请求根本不会建连(HIT、STALE)。想看“真实发起连接”的耗时,必须绑定 $upstream_cache_status:
log_format cache_connect '$remote_addr [$time_local] "$request" ' '$upstream_cache_status $upstream_connect_time $upstream_header_time ' '$upstream_addr $status';
然后只分析 MISS、EXPIRED、BYPASS 的日志行。例如用 awk 快速筛出高连接耗时的回源请求:
awk '$5 == "MISS" && $6 > 0.1 {print}' /var/log/nginx/cache_connect.log
这行命令找出所有缓存未命中、且建连超 100ms 的请求,再结合 $8(即 $upstream_addr)就能锁定是哪台后端响应慢。
避免常见误判陷阱
-
$upstream_connect_time = 0.000不代表建连快,很可能 DNS 失败、连接被拒、或根本没走到 connect 阶段。此时要查error.log是否有connect() failed、no resolver defined等提示。 - 它不含 TLS 握手时间(除非你用的是 Nginx ≥ 1.19.10 且启用了
ssl_preread相关优化),若开启 mTLS,实际建连+握手可能达几十毫秒,但$upstream_connect_time仍只报几毫秒。这时必须对比$upstream_header_time − $upstream_connect_time。 - 多个 upstream server 时,字段值会以逗号分隔(如
10.0.1.1:80,10.0.1.2:80对应0.004,0.009),需确保日志解析逻辑能正确映射。
不复杂但容易忽略











