gin服务暴露/metrics端点必须显式注册promhttp.handler(),否则prometheus抓取返回404;mustregister()仅注册指标不提供路由;需用gin.wraph(promhttp.handler())挂载路由,路径须与scrape_configs一致。

Gin 服务暴露 /metrics 端点必须显式注册 promhttp.Handler(),否则 Prometheus 抓取会返回 404 或空响应——这不是配置问题,是路由根本没挂上。
为什么 MustRegister() 不等于指标可访问
很多人调了 prometheus.MustRegister() 就以为完事了,结果 curl http://localhost:8080/metrics 返回 404。这是因为:MustRegister() 只负责把指标注册进全局注册器,不提供 HTTP 路由;Gin 的 DefaultServeMux 也不会自动加载 Prometheus 的 handler。
- 必须手动用
gin.WrapH(promhttp.Handler())包装后挂到路由上 - 路径名(如
/metrics)要和 Prometheus 配置里的scrape_configs.job.metrics_path完全一致 - 如果用了自定义
http.ServeMux或反向代理(如 nginx),还要确认该路径未被拦截或重写
Gin 中注册指标的正确姿势
所有指标变量必须定义为包级变量,且只在启动时注册一次。重复注册同名指标会直接 panic,且无法 recover。
- 错误写法:在 handler 里每次请求都
prometheus.NewCounter()+MustRegister() - 正确写法:
var httpReqCounter = prometheus.NewCounterVec(...),然后在init()或main()开头调用prometheus.MustRegister(httpReqCounter) - 动态打标用
.WithLabelValues("GET", "/api/user", "200"),别为每个组合新建指标
活跃连接数不能用 Counter,得用 Gauge
用 prometheus.NewCounter() 统计“当前在线用户”或“WebSocket 连接数”,是典型误用。Counter 只能增不能减,goroutine 退出时没回调机制,指标只会单向上涨。
- 应改用
prometheus.NewGauge(),并在 goroutine 启动时.Inc()、退出前.Dec() - 注意
Gauge.Dec()可能导致负值,生产环境建议加 guard 判断或用Set()替代 - 若依赖 context cancel,确保
defer gauge.Dec()在正确作用域内执行
最常被跳过的一步是:没验证 http.ListenAndServe() 是否真在跑,或者 Gin 的 Run() 被提前 exit —— 指标注册再对,服务没起来也白搭。上线前务必 curl 一下 /metrics,看有没有真实内容返回。











