e.GET("/metrics", echo.WrapHandler(promhttp.Handler())) 是必须的,因为 echo-contrib/prometheus 仅注册指标而不暴露路由,需显式挂载 promhttp.Handler() 到 Echo 路由才能被 Prometheus 正确抓取。

为什么 e.GET("/metrics", echo.WrapHandler(promhttp.Handler())) 是必须的
因为 echo-contrib/prometheus 只注册指标,不暴露路由。调用 p.Use(e) 后,/metrics 路径依然 404——Prometheus 抓取目标直接显示 DOWN。根本原因是 Echo 中间件只作用于已定义的路由,而 promhttp.Handler() 是一个独立的 HTTP handler,必须显式挂载到 Echo 的路由系统或 Go 的 http.ServeMux 中。
推荐走 Echo 路由链:用 echo.WrapHandler() 包装,确保日志、CORS、Recovery 等中间件能生效;若用 http.Handle(),则绕过 Echo,失去中间件能力。
- 别写
http.HandleFunc("/metrics", promhttp.Handler().ServeHTTP):这是 v0.x 旧写法,v1.0+ 已废弃,运行时 panic - 路径名不必非得是
/metrics,但必须和 Prometheus 配置中scrape_configs.job.metrics_path完全一致 - 如果同时用了多个框架(如 Echo + 单独启 http server),注意端口冲突或 mux 覆盖
prometheus.NewPrometheus 注册了什么,又没注册什么
prometheus.NewPrometheus("echo", nil) 默认向 prometheus.DefaultRegisterer 注册了基础指标:http_request_duration_seconds(直方图)、http_requests_total(计数器)、http_request_size_bytes 等。但它不注册任何业务指标,也不绑定路由,更不接管 /metrics 输出逻辑。
常见误判是以为 “用了这个中间件就自动监控了”,结果 curl http://localhost:1323/metrics 返回空或 404。实际它只做了两件事:在请求生命周期中打点、把指标写进默认注册器。
- 所有指标最终输出内容,取决于你传给
promhttp.HandlerFor()的注册器实例 - 如果你创建了自定义
prometheus.NewRegistry(),必须显式调p.SetRegisterer(reg),否则/metrics返回空,Targets 显示 UP 但查不到http_*指标 - 未调
SetRegisterer是高频坑,且无报错提示,只能靠验证指标是否存在来发现
自定义指标怎么加才不 panic、不漏数、不爆炸
加业务指标不是“多写几行就行”,稍不注意就会触发 duplicate metrics collector registration panic,或产生高基数 label 拖垮 Prometheus。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
核心原则:指标变量必须是包级变量,注册只做一次,label 值必须可枚举、低基数。
- 危险写法:
func handler(c echo.Context) { counter := prometheus.NewCounter(...); prometheus.MustRegister(counter) }—— 每次请求都新建+注册,启动即 panic - 安全写法:
var bizReqCounter = prometheus.NewCounterVec(prometheus.CounterOpts{...}, []string{"route", "status"}); func init() { prometheus.MustRegister(bizReqCounter) } - label 值别填动态 ID:
user_id="123456"是灾难;应填模板化值:route="POST /order/{id}"、env="staging" - 活跃连接数不能用 Counter:必须用
Gauge,且每个 goroutine 退出前调.Dec()(建议 defer)
本地调试 /metrics 返回空?先盯住变量生命周期
现象:代码看着没问题,curl /metrics 返回空响应,也没有错误日志。最可能的原因是指标变量被 GC 回收了——Go 编译器发现该变量只在函数内注册、无全局引用,就把它当垃圾清掉了。
这在快速验证、测试代码里极常见,排查起来毫无头绪。关键检查点只有一个:指标是不是包级变量?有没有被任何全局作用域持有?
- 所有
prometheus.NewXXX()实例必须声明在包顶层,不能在main()函数里、不能在 handler 里、不能在 goroutine 里 - 如果用了
promauto(如promauto.NewCounterVec),它内部已自动注册,不要再手动MustRegister - 用
go tool compile -gcflags="-m" main.go可辅助判断变量是否逃逸,但最直接的办法还是看/metrics是否有对应指标文本输出
真实集成里最易被忽略的,是注册器一致性与变量作用域这两层——它们不出错时一切正常,一出错就是静默失效,既不报错也不采集,只能靠人工核对指标是否存在。









