Go程序暴露自定义指标需先用NewCounterVec定义带标签计数器并调用MustRegister注册,再通过http.Handle("/metrics", promhttp.Handler())挂载;命名须全小写加下划线,标签维度需匹配WithLabelValues调用,避免闭包捕获循环变量及重复注册panic。

Go 程序暴露自定义指标,核心是用 prometheus.NewGaugeVec 或 prometheus.NewCounterVec 注册指标,再通过 http.Handle 挂载 promhttp.Handler() —— 不注册就查不到,不挂载就收不到请求,这两步缺一不可。
怎么定义和注册一个带标签的计数器
直接 new 出来不算注册,必须调用 prometheus.MustRegister() 才会被 Prometheus 客户端识别。漏掉这步,/metrics 页面里永远看不到它。
-
prometheus.NewCounterVec的第一个参数是prometheus.CounterOpts,其中Name不能含空格或特殊字符,推荐全小写+下划线,比如"http_request_total" -
Labels字段定义维度(如[]string{"method", "status"}),后续.WithLabelValues("GET", "200")才能正确打点 - 变量名建议带
metric后缀(如httpRequestTotalMetric),避免和业务变量混淆
为什么 /metrics 返回 404 或空内容
常见原因是没把 promhttp.Handler() 挂到正确的路由上,或者用了 http.HandleFunc 而不是 http.Handle。
- 必须用
http.Handle("/metrics", promhttp.Handler())——HandleFunc会忽略Handler的 ServeHTTP 方法,导致返回空 - 如果用了 Gin/echo 等框架,别直接传
promhttp.Handler(),要包装成对应框架的中间件形式(例如 Gin 中用gin.WrapH(promhttp.Handler())) - 确认 HTTP server 已启动,且没有被其他
http.ListenAndServe覆盖(尤其测试时多个 main 函数并存)
如何在 HTTP handler 里安全更新指标
指标对象本身是并发安全的,但别在闭包里捕获循环变量,否则所有请求都打到同一个 label 值上。
- 错误写法:
for _, m := range methods { http.HandleFunc("/"+m, func(w r) { reqCounter.WithLabelValues(m).Inc() }) }—— 这里的m是循环变量,最终全变成最后一个值 - 正确写法:用显式参数传入,或在循环内定义新变量,如
method := m; http.HandleFunc(... func() { reqCounter.WithLabelValues(method).Inc() }) - 高频打点场景(如每毫秒一次)可考虑用
prometheus.NewCounter(无 label)替代Vec版本,减少 map 查找开销
真正容易被忽略的是指标生命周期:如果服务热重启或多次 init,重复调用 MustRegister 会 panic;上线前务必确保每个指标只注册一次,且全局变量初始化顺序不会导致 race。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











