关键在于准确打标与正确计算:需显式用 status label 区分真实 http 状态码,统一 label 名为 status;错误率必须用 sum(rate()) / sum(rate()) 计算,窗口 5m,告警加 for: 2m 防抖。

怎么用 Prometheus 抓到真实的 HTTP 状态码分布
关键不是“能不能抓”,而是“抓得准不准”——默认 http_requests_total 指标本身不带状态码维度,必须显式打上 status label 才能做分组计算。很多团队上线后发现告警总不触发,一查是指标根本没按状态码暴露。
- 中间件里必须调用
.WithLabelValues(r.Method, r.URL.Path, statusCode),不能只写"200"这种固定值;真实状态码得从包装过的ResponseWriter中获取 - 避免给健康检查接口(如
/healthz)埋点:它高频返回200,会稀释业务错误率,导致rate(http_requests_total{status=~"5.."}[5m])计算失真 - label 名必须统一为
status(不是code或http_status),否则 PromQL 查询时匹配不到
为什么 rate() / sum(rate()) 是唯一靠谱的错误率写法
直接用 http_requests_total{status="500"} 做绝对值告警?错。计数器会重置、服务可能重启、不同实例基数差异大——只有速率比才具备可比性和稳定性。
- 正确写法:
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05 - 别漏掉外层
sum():多实例部署时,各实例的rate()结果要先聚合再除,否则 PromQL 会因向量匹配失败报many-to-many matching not allowed - 窗口选
[5m]而非[1m]:太短会放大瞬时抖动;超过[10m]则响应滞后,线上故障黄金 5 分钟就错过了
告警规则里 for: 2m 不是摆设,是防误报的底线
HTTP 错误率波动太常见:下游超时、DB 连接池打满、临时网络抖动……单次采样超标不等于故障。Prometheus 的 for 字段就是用来确认“这事儿是不是真在持续发生”。
-
for: 2m意味着连续 4 个 scrape 周期(默认 30s 一次)都满足条件才真正触发,跳过毛刺 - 别设成
for: 30s:等同于无保护,K8s 里一个 Pod 重建、Service endpoint 切换都会引发误报 - 搭配
labels: {severity: "warning"}和annotations: {description: "5xx error rate >5% for 2m"},Alertmanager 才能路由和通知准确
为什么你 curl /metrics 看得到 5xx 计数,但告警就是不响
最常卡在这一步:指标存在 ≠ 规则能算出来。问题往往出在数据链路断点,而不是 PromQL 写错。
立即学习“go语言免费学习笔记(深入)”;
- 先去 Prometheus UI 的
Status > Targets页面确认你的 Go 服务 target 状态是UP,且Labels里有instance和job - 手动
curl http://your-go-app:8080/metrics | grep "500",确认输出里真有类似http_requests_total{method="GET",endpoint="/api/user",status="500"} 12的行 - 在 Prometheus Graph 里执行
rate(http_requests_total{status=~"5.."}[5m]),看是否返回非空向量;如果结果为空,说明 label 名或值不匹配,或者采集周期还没攒够数据
真正难的从来不是写一行 PromQL,而是让每个环节的数据都对得上号——label 一致、采集正常、时间窗口对齐、聚合逻辑无歧义。漏掉其中任何一环,告警就只是个静态配置文件。










