根本原因是未调用prometheus.mustregister()注册自定义指标,仅挂载/metrics路由只能暴露go运行时指标;必须在http.listenandserve()前完成注册,且标签名、顺序、值需严格一致,否则指标静默丢失。

Go 服务暴露 /metrics 端点但 Prometheus 抓不到业务指标,不是配置问题,而是指标根本没注册进采集器——90% 的集成失败都卡在这一步。
为什么 curl localhost:8080/metrics 有数据但没你的业务指标?
默认的 promhttp.Handler() 只暴露 go_*、process_* 这类运行时指标,你定义的 httpReqCounter 如果没调用 prometheus.MustRegister(),它就不存在于默认注册器里。
- 运行时指标自动注册;业务指标必须显式注册,且只允许一次
- 常见错误:把
prometheus.NewCounterVec()写在 handler 函数里,每次请求都新建+注册 → 启动 panic 或指标丢失 - 正确做法:声明为包级变量 + 在
init()或main()开头注册
http.ListenAndServe() 传 promhttp.Handler() 编译报错?
错误提示类似 cannot use promhttp.Handler() (value of type http.Handler) as type func(http.ResponseWriter, *http.Request),是因为 http.ListenAndServe() 第二个参数接受 nil 或函数类型,而 promhttp.Handler() 返回的是 http.Handler 接口实例。
- 安全写法:
http.Handle("/metrics", promhttp.Handler())+http.ListenAndServe(":8080", nil) - 更推荐新建独立 mux:
mux := http.NewServeMux(); mux.Handle("/metrics", promhttp.Handler()); http.ListenAndServe(":8080", mux),避免污染http.DefaultServeMux - 若用 Gin/Echo,必须包装:
gin.WrapH(promhttp.Handler())或echo.WrapHandler(promhttp.Handler())
用 CounterVec 打状态码维度时为啥指标爆炸?
直接用 r.StatusCode 当 label 值,会导致每个新状态码(比如 429123)都生成一个新时间序列,Prometheus server 内存和查询性能迅速崩坏。
- label 值必须是低基数、预定义集合,例如
"200"、"404"、"500"、"other" - 不要用
strconv.Itoa(r.StatusCode)直接转,而是用switch映射常见码,非标统一归入"other" - 路径、用户 ID、UUID 绝对不能进 label —— 这是 cardinality problem 的高发区
活跃连接数该用 Counter 还是 Gauge?
用 Counter 统计“当前在线人数”或 WebSocket 连接数,等于埋雷:它只能增不能减,goroutine 退出后指标永远卡在高位,监控完全失真。
- 活跃连接、内存用量、线程数等瞬时值,必须用
prometheus.NewGauge() - 每个 goroutine 启动时
.Inc(),退出前务必defer gauge.Dec() - 如果 goroutine 可能 panic,需配合
recover()确保Dec()执行,否则泄漏不可逆
最易被忽略的一点:指标变量若定义在函数内、未被全局引用持有,Go GC 可能在注册后回收它,/metrics 返回空内容却无任何报错——这种静默失败最难调试。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











