自定义指标未出现在/metrics页面,根本原因是未调用prometheus.mustregister()显式注册,或注册时机错误(如在handler中注册)、注册器不匹配(如用自定义registry却调用默认promhttp.handler());counter适用于只增累计值,gauge适用于可升降瞬时值;标签名需预定义、顺序严格一致,动态路径须归一化,/metrics端点必须手动挂载http.handle("/metrics", promhttp.handler())。

你的自定义指标没出现在 /metrics 页面,不是 Prometheus 没抓到,而是你根本没注册上去——prometheus.MustRegister() 这一步漏了,或者注册时机、注册器用错了。
为什么 /metrics 里看不到你定义的 Counter 或 Gauge
默认的 promhttp.Handler() 只暴露 prometheus.DefaultRegisterer 里的指标。Go 运行时自带的 go_*、process_* 是自动注册的;但你自己 new 出来的 Counter 或 Gauge 不会自动进去。
- 常见错误:写了
var orderCreatedCounter = prometheus.NewCounter(...),但没在init()或main()开头调prometheus.MustRegister(orderCreatedCounter) - 更隐蔽的问题:指标变量定义在包级,但
init()执行顺序受导入依赖影响,若注册逻辑被跳过,指标就“消失” - 用了自定义注册器(比如
reg := prometheus.NewRegistry()),却仍调promhttp.Handler(),结果暴露的是空注册器,不是你的reg - 修复方式:必须用
promhttp.HandlerFor(reg, ...),且确保所有指标都注册到了这个reg上
Counter 和 Gauge 到底该选哪个
关键看数据语义是否允许“减少”:Counter 是只增不减的累计值,Gauge 是可正可负的瞬时快照。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
Counter记事件总数:订单创建数、HTTP 请求次数、消息投递成功数;只能调Inc()或Add(n),传负数会 panic - 用
Gauge表当前状态:活跃连接数、队列积压量、内存使用 MB;支持Set(x)、Add(delta)、Inc()、Dec(),但不会自动归零——goroutine 退出时记得Dec(),否则活跃数永远不准 - 别用
Gauge.Set(1)模拟一次请求:这会让 PromQL 聚合失效(比如sum()就不是请求数) - 命名约定:
_total后缀是Counter约定;_count或_size更适合Gauge
带 label 的指标怎么写才不 panic
Label 名(key)不是运行时决定的,而是在 NewCounterVec 或 NewGaugeVec 初始化时就锁死的。一旦注册,不能再加新 label 名,也不能删。
- 错误写法:
counterVec.WithLabelValues("GET", "200").Inc()对应的 vec 初始化时只写了[]string{"method"},缺了"status"→ 直接 panic - 标签名必须全小写、下划线分隔(如
tenant_id),不能写tenant-id或TenantID -
WithLabelValues()传值顺序必须和定义时[]string{"a","b","c"}完全一致,错一位就 panic - 避免高基数 label:用户 ID、手机号、traceID 别直接塞进 label,改用
Counter按error_type分类或采样上报 - 路径中动态 ID(如
/order/12345)必须归一化为占位符(如/order/{id})再打点
promhttp.Handler() 怎么挂才生效
http.DefaultServeMux 不会自动绑定 /metrics,只调 MustRegister() 没用。必须手动挂载,且注意新版 API 已移除旧写法。
- 别写
http.HandleFunc("/metrics", promhttp.Handler().ServeHTTP)—— 这是 v0.x 写法,v1.0+ 已废弃,编译失败 - 正确做法:
http.Handle("/metrics", promhttp.Handler())(用默认注册器)或http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))(用自定义注册器) - 建议单独开一个 metrics server(如监听
:9091),避免和业务路由混用导致冲突或污染 - 别在 HTTP handler 里动态新建指标再注册——这是高频 panic 来源;也别在 handler 里实时查 DB 再设
Gauge,这会让每次请求都承担采集开销,且无法对齐 Prometheus 抓取周期
最容易被忽略的其实是指标生命周期:Counter 和 Gauge 实例一旦注册就不能换,但 label 值可以随时更新;而 Vec 类型的指标对象本身是线程安全的,但反复调 WithLabelValues() 会做 map 查找——高频路径建议缓存常用 label 组合的子指标实例。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










