$upstream_status 更适合诊断后端抖动,因其记录每次上游请求的真实状态码序列(如"504,502,200"),而 $status 仅显示最终返回客户端的状态码(如200),掩盖了重试过程中的失败细节。

为什么 $upstream_status 比 $status 更适合诊断后端抖动
$status 只反映 Nginx 返回给客户端的最终状态码(比如 200、502),掩盖了上游的真实表现;而 $upstream_status 记录的是每次 upstream 请求实际拿到的响应状态码,哪怕只有一轮重试成功,它也会按顺序记录所有尝试的结果(如 "504,502,200")。后端偶发超时或短暂 5xx,靠 $status 看不到,但 $upstream_status 能暴露出来。
常见错误现象:日志里 $status 全是 200,监控却显示后端 P99 延迟突增——大概率是上游反复失败又靠重试兜底,$upstream_status 里藏着逗号分隔的失败码。
- 必须开启
proxy_next_upstream(默认不包含error或timeout,要显式配置)才能触发多次尝试并填充$upstream_status -
$upstream_status不是每个请求都非空:如果没走重试、或 upstream 直接返回 200,它可能为空字符串或单个码 - 该变量在
log_format中可用,但在if或map中不可用(Nginx 变量作用域限制)
如何在 access_log 中安全输出 $upstream_status 并避免日志膨胀
直接写 $upstream_status 到日志容易导致字段长度失控(比如重试 10 次就生成一长串数字加逗号),影响解析和磁盘占用。更实用的做法是提取关键特征:
- 用
log_format中的$upstream_status原始值做基础,但配合外部工具(如 Loki 的 logql、ELK 的 grok)做后期解析 - 若需 Nginx 内部简化,可用
map配合正则提取首尾状态:例如只记录首次尝试是否失败(~^(5|4)[0-9]{2}→"first_fail"),但注意map不能引用$upstream_status - 更稳妥的替代:用
$upstream_connect_time和$upstream_header_time组合判断抖动——比如header_time > 2.0且connect_time ,说明连接快但处理慢,指向后端应用层卡顿
示例精简日志格式:
log_format upstream_health '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'$upstream_addr $upstream_status '
'$upstream_connect_time $upstream_header_time';
从日志中识别典型后端抖动模式
拿到含 $upstream_status 的日志后,不要只扫 5xx,重点看组合规律:
-
"504,200":第一次超时,重试成功 → 检查proxy_read_timeout是否过短,或后端偶发慢查询 -
"502,502,200":前两次后端进程崩溃/未响应,第三次恢复 → 结合$upstream_addr看是否固定某台机器,再查其进程存活与负载 -
"200,200,200"但$upstream_header_time波动极大(如 0.02s / 1.8s / 0.03s)→ 后端存在锁竞争或 GC 暂停,非错误码型抖动 - 大量
"-"(短横)出现在$upstream_status字段 → 请求根本没发到 upstream(如 DNS 失败、upstream 没配 weight 或 down 状态),此时应查$upstream_addr是否为空
容易被忽略的配置依赖和边界情况
$upstream_status 不是开箱即用的“健康探针”,它的有效性高度依赖 upstream 模块行为:
- 仅对
proxy_pass、fastcgi_pass等 upstream 类指令生效,对return或rewrite跳转无效 - 当使用
proxy_buffering off且后端流式响应时,$upstream_status仍能取到,但$upstream_header_time可能不准(头未完整接收就已开始传 body) - 如果 upstream 是
keepalive连接池,$upstream_status记录的是本次请求的实际结果,不反映连接池本身的健康状态(比如连接池里某个 socket 已断但尚未被探测到) - Kubernetes Ingress NGINX 控制器默认关闭
proxy_next_upstream的http_503,若后端主动返回 503 表示过载,需手动开启才会计入$upstream_status
真正难的不是记录这个变量,而是把 "504,200" 和 "200" 在统计口径上区分开,并让告警规则理解“一次成功背后可能有两次失败”。这需要日志解析逻辑、采样策略和上下游超时参数协同设计。











