Gin应用中自定义指标上报失败主因是注册、打点、暴露三步出错:指标须声明为包级变量,注册必须在init()或main()开头且仅一次;NewCounterVec不可在handler内重复创建,否则因同名重复注册触发无法recover的panic;标签名顺序与WithLabelValues参数必须严格一致;/metrics需用router.GET("/metrics", gin.WrapH(promhttp.Handler()))显式挂载。

Gin 应用里自定义指标上报失败,90% 是卡在注册、打点、暴露三步中的某一个环节——不是没写代码,是没写对位置或没配对参数。
为什么 prometheus.NewCounterVec 不能写在 handler 里
每次请求都 new 一次,就会触发 panic: duplicate metrics collector registration。Prometheus 客户端不允许同名指标重复注册,且这个 panic 无法 recover。
- 所有指标变量(如
httpRequestsTotal、orderStatusGauge)必须声明为包级变量 - 注册逻辑统一放在
init()或main()开头,只执行一次 -
WithLabelValues("GET", "/api/order", "200")的参数顺序,必须和[]string{"method", "path", "status"}完全一致,错一位就 panic - 别在多个包里 import 同一个 metrics 初始化文件,否则
MustRegister()被调用多次
Gin 路由怎么正确挂载 /metrics
promhttp.Handler() 只暴露默认注册器上的指标,而 Gin 默认不接管 /metrics,也不会自动绑定到 http.DefaultServeMux。
- 必须显式注册:用
router.GET("/metrics", gin.WrapH(promhttp.Handler())) - 路径要和 Prometheus 配置里的
scrape_configs.job.metrics_path完全一致(默认是/metrics) - 如果用了自定义注册器(如
reg := prometheus.NewRegistry()),得改用promhttp.HandlerFor(reg, promhttp.HandlerOpts{}),否则指标不出现在响应里 - 别在外层再套一层自定义
http.HandlerFunc,会导致Accept: application/vnd.prometheus.text/plain协商失败,返回406 Not Acceptable
选错指标类型或标签导致监控失真
用 NewGauge 替代 NewGaugeVec 上报带维度的业务数据,查出来的就是一团浆糊;标签值非法或 cardinality 爆炸,会让 Prometheus 存储和查询直接变慢甚至 OOM。
- 业务指标(如订单数按状态、渠道拆分)必须用
GaugeVec或CounterVec,不能用裸NewGauge - 标签名只能是小写字母、数字、下划线,比如
pay_channel合法,pay-channel会 panic -
ConstLabels只适合 instance、version 这类进程级静态属性,别塞 user_id、order_id 这种高基数动态值 - 高频更新场景下,避免在循环里反复调用
WithLabelValues();可缓存prometheus.Labels或子指标对象
最难 debug 的其实是标签校验缺失——handler 里直接把用户传参喂给 WithLabelValues(),一旦出现空字符串、超长值、特殊字符,服务就 panic;得加白名单或长度限制,而不是靠“应该不会出问题”来赌。


















