/metrics 返回空或404主因是指标未注册至默认registry或路由配置错误;counter适用于累计量,gauge适用于瞬时值;带label指标需声明、注册、打点三者严格一致;延迟指标优先选histogram(预设bucket)或summary(客户端算分位),并严控label基数防oom。

为什么 /metrics 返回空或 404
不是代码没写,是指标根本没注册进默认 registry。promhttp.Handler() 只读 prometheus.DefaultRegisterer,你定义了 httpRequestsTotal 变量但没调 prometheus.MustRegister(httpRequestsTotal),它就永远不出现。
常见错误现象:
-
/metrics返回 200 但内容为空(指标未注册) -
/metrics返回 404(路由挂错,用了http.HandleFunc而非http.Handle) - 用自定义
prometheus.NewRegistry()却仍调promhttp.Handler()(该函数只查默认注册器)
实操建议:
- 注册必须在
http.ListenAndServe()之前完成,放在init()或main()开头最稳 - 若用自定义 registry,必须配
promhttp.HandlerFor(reg, nil) - 运行时基础指标(如 goroutine 数、内存分配)要显式注册:
prometheus.MustRegister(prometheus.NewGoCollector())
Counter 和 Gauge 什么时候用错会崩 PromQL
选错类型不是“不准”,是让 rate()、increase() 这类关键函数直接失效。
Counter:只增不减,适合累计量——http_requests_total、errors_total;服务重启后数值归零是正常行为,rate() 就是靠这个跳变算速率。
Gauge:可增可减,适合瞬时值——go_goroutines、cache_size_bytes;用它记请求数,重启后归零会导致 rate() 输出负数或 0,PromQL 查不到有效趋势。
别踩的坑:
- 用
Gauge.Set()记请求总数 →rate()崩 - 用
Counter.Inc()记当前活跃连接数 → 它不会减,数字只涨不跌 - 用
Gauge模拟 P95 延迟 → 这是Histogram或Summary的职责
带 label 的计数器怎么注册和打点才不 panic
标签维度声明、注册、打点三者必须严格一致,差一个字段名或顺序,.WithLabelValues() 就 panic。
示例定义:
var httpRequestTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests.",
},
[]string{"method", "endpoint", "status"},
)
实操要点:
- 注册:必须调
prometheus.MustRegister(httpRequestTotal),且只能一次 - 打点:
httpRequestTotal.WithLabelValues(r.Method, "/api/v1", "200").Inc()—— 三个值顺序、数量、类型必须和上面[]string完全匹配 - 禁止传空字符串或
nil:比如status是""会 panic - 循环注册 handler 时别捕获循环变量:
for _, path := range paths { http.HandleFunc(path, func() { counter.WithLabelValues(path).Inc() }) }→ 全部打到最后一个path;应改用path := path显式绑定
延迟指标该用 Histogram 还是 Summary
两者都行,但选错会带来可观测性盲区或性能浪费。
Histogram:预设 bucket 边界,在服务端分桶计数。适合你明确知道延迟分布范围、且希望 Prometheus 端做分位数计算的场景。Web API 推荐 buckets:[]float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 5}(单位秒)。如果发现 rate(http_request_duration_seconds_bucket[5m]) 全堆在最大 bucket(比如全是 5),说明大部分请求超时,得加宽上限或排查慢请求根因。
Summary:客户端计算分位数,直接上报 quantile 值。适合你只关心几个固定分位(如 P95/P99)、且不想在 Prometheus 端承担计算压力的场景。但必须显式设置 Objectives,否则默认只返回 quantile="0.5" 和 "0.9",P99 就丢了。
绝对不能做的事:
- 用
Counter记耗时 → 单位错、语义错、无法聚合 - 用
Histogram却把 buckets 设成[1, 10, 100]毫秒 → 实际延迟常超 200ms,所有数据进+Inf桶,失去区分度 - 用
Summary却不设Objectives→ 关键分位点缺失
真正容易被忽略的是 label 基数控制:哪怕类型、注册、打点全对,只要把 user_id 或 request_id 当 label 塞进去,Prometheus 内存就可能几小时内 OOM。路径类 label 务必泛化,/user/123 → /user/{id},别等报警响了才想起来改。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











