/metrics返回空但能看到go_goroutines,是因为promhttp.Handler()默认只从prometheus.DefaultRegisterer读指标,而自定义指标如httpRequestsTotal未调用prometheus.MustRegister()注册,或注册发生在HTTP服务启动之后;运行时指标自动注册,业务指标必须显式注册、绑定、打点三步闭环。

为什么Echo里/metrics返回空,却能看到go_goroutines
因为promhttp.Handler()默认只从prometheus.DefaultRegisterer读指标,而你的httpRequestsTotal没调prometheus.MustRegister(),或者注册发生在HTTP服务启动之后。运行时指标(go_*、process_*)是自动注册的,业务指标不会“自己冒出来”。
- 注册必须在
echo.Start()或http.ListenAndServe()之前完成,推荐统一放在func init()里 - 别把指标定义写在handler函数里——每次请求都
NewCounterVec()+MustRegister(),会直接panic - 如果用了
promauto.NewCounterVec(),它内部已自动注册,无需再手动MustRegister() - 检查是否多个包都import了同一套metrics封装,导致多个
init()重复注册同名指标
echo.WrapHandler(promhttp.Handler())编译报错或500
常见于Echo v4.10以下版本,echo.WrapHandler()不兼容http.Handler接口;或没启用Recovery中间件,导致指标handler panic后整个/metrics返回500。
- 升级到Echo v4.10+再用
e.GET("/metrics", echo.WrapHandler(promhttp.Handler())) - 更稳妥的做法:用
e.GET("/metrics", func(c echo.Context) error { promhttp.Handler().ServeHTTP(c.Response(), c.Request()) return nil }),显式控制响应流 - 务必加
e.Use(middleware.Recovery()),否则任何panic都会让/metrics不可用 - 避免把
/metrics挂进带CORS/日志/认证的主路由mux——中间件可能篡改Content-Type: text/plain,导致Prometheus抓取失败
自定义CounterVec打点时报duplicate metrics collector registration
这不是并发冲突,而是同一指标名被多次注册。典型场景:不同包都写了prometheus.MustRegister(httpRequestsTotal),或用了promauto但没指定注册器,各包初始化时都往DefaultRegisterer塞同名指标。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 根治方法:所有指标声明为包级变量,注册收口到
main()最开头,其他模块只接收*prometheus.CounterVec作为参数 - 临时调试可加
prometheus.Unregister(httpRequestsTotal)再注册,但仅限开发环境 - 生产推荐隔离方案:
reg := prometheus.NewRegistry(),然后promhttp.HandlerFor(reg, nil)绑定路由,彻底避免污染全局 - Label值为空(如
status="")会导致cardinality爆炸,打点前务必校验:if status == "" { status = "unknown" }
HTTP中间件里记录耗时,Observe()总不准
http_request_duration_seconds直方图必须在请求真正结束、响应已写出后才能Observe(),否则测到的是中间件执行时间,不是端到端延迟。
立即学习“go语言免费学习笔记(深入)”;
- 错误写法:在
next(c)前就histogram.Observe(...) - 正确写法:用
defer包裹Observe(),并确保在next(c)之后、且response已flush - 如果handler可能panic,需配合
recover()确保Observe()仍执行,或改用context超时控制连接生命周期 - 别用
Summary替代Histogram——它不支持rate(),P99计算失真;也别把status当Histogram的label,直方图不支持多维分桶
go_goroutines——业务指标不会自动出现,也不会报错提醒,只会静默丢失。

















