echo 默认不暴露指标端点,因框架未内置 prometheus 集成;需手动注册 /metrics 路由、使用 echo.wraphandler() 适配 promhttp.handler(),并显式定义和注册 countervec/histogramvec 指标。

为什么 echo.New() 默认不暴露指标端点
Echo 框架本身不内置 Prometheus 指标收集,echo.New() 创建的实例不会自动注册 /metrics 路由,也不会自动采集 HTTP 请求延迟、状态码分布等基础指标。你得手动接入 promhttp 并挂载中间件——否则 Prometheus 抓取时会返回 404。
常见错误是只引入 github.com/prometheus/client_golang/prometheus/promhttp,却忘了用 e.GET("/metrics", promhttp.Handler()) 显式注册路由。更隐蔽的问题是:如果 Echo 启用了 HTTPErrorHandler 或自定义日志中间件,可能拦截了 /metrics 的 200 响应,导致指标抓取失败但无明显报错。
- 必须在
e.GET()中显式挂载promhttp.Handler(),不能依赖全局中间件 - 确保该路由在所有认证/鉴权中间件之前注册,否则未授权访问会被拦住
- 避免用
e.Use(middleware.BasicAuth(...))全局保护,除非明确放行/metrics
如何用 echo.WrapHandler() 接入 promhttp.Handler()
promhttp.Handler() 是标准 http.Handler,而 Echo 的路由方法只接受 echo.HandlerFunc。直接传会类型不匹配,编译报错:cannot use promhttp.Handler() (type http.Handler) as type echo.HandlerFunc。
正确做法是用 echo.WrapHandler() 包一层:
e.GET("/metrics", echo.WrapHandler(promhttp.Handler()))
注意不要写成 echo.WrapHandler(promhttp.Handler()).ServeHTTP 或手动调用 Handler().ServeHTTP —— 这会导致 panic,因为 ServeHTTP 需要完整 http.ResponseWriter 和 *http.Request,Echo 的上下文封装已接管这部分。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
-
echo.WrapHandler()是唯一安全的桥接方式,别自己实现适配器 - 如果项目用了
echo.Group分路由,把/metrics挂在根e上,避免 Group 前缀干扰(比如/api/metrics不被 Prometheus 默认配置识别) - 生产环境建议加简单 token 验证,例如
e.GET("/metrics", echo.WrapHandler(promhttp.Handler()), middleware.KeyAuth(func(key string) (bool, error) { return key == os.Getenv("METRICS_TOKEN") }))
怎样让 Echo 自动上报请求计数、延迟、状态码
Prometheus 官方 client 不会自动采集 HTTP 指标;Echo 也不自带 metrics 中间件。你需要手动注册 prometheus.CounterVec、prometheus.HistogramVec,并在中间件中更新它们。
典型做法是写一个自定义中间件,在 c.Next() 前后记录开始时间、响应状态码、路径标签等:
var (
httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{Namespace: "echo", Subsystem: "http", Name: "requests_total"},
[]string{"method", "path", "status"},
)
httpRequestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{Namespace: "echo", Subsystem: "http", Name: "request_duration_seconds"},
[]string{"method", "path"},
)
)
func MetricsMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
err := next(c)
status := c.Response().Status
method := c.Request().Method
path := c.Path()
httpRequestsTotal.WithLabelValues(method, path, strconv.Itoa(status)).Inc()
httpRequestDuration.WithLabelValues(method, path).Observe(time.Since(start).Seconds())
return err
}
}
}
然后在 e.Use(MetricsMiddleware()) 注册。注意:路径标签用 c.Path() 而非 c.Request().URL.Path,否则带参数的路由(如 /user/:id)会生成爆炸性 label 组合,撑爆 Prometheus 内存。
- 务必调用
prometheus.MustRegister()在服务启动时注册这些指标向量,否则指标不会暴露 - 避免在 label 中使用用户输入(如 query 参数、body),防止 label cardinality 爆炸
- 如果用了 Echo 的
Router.Find()动态路由,c.Path()返回的是注册时的 pattern(如/user/:id),这正是你想要的
为什么 Prometheus 抓到的指标里没有 echo_http_requests_total
最常见原因是指标命名空间没对上。Echo 本身不声明任何指标,所有指标名都由你注册时的 prometheus.CounterOpts 决定。如果你写的是 Name: "requests_total",最终指标名就是 requests_total,不是 echo_http_requests_total —— 后者需要你显式设 Namespace: "echo" + Subsystem: "http"。
另一个容易忽略的点:Prometheus 抓取目标配置里的 job 标签值,和你在代码里用 prometheus.NewRegistry() 自定义注册器无关;但如果你没调用 prometheus.MustRegister(),或者注册器没传给 promhttp.HandlerFor(),那 /metrics 返回的内容里就真的没有那些指标。
- 检查
/metrics响应体是否包含你定义的指标名(如echo_http_requests_total),而不是只看 Prometheus UI 的 target 状态 - 若用了自定义
prometheus.Registry,需用promhttp.HandlerFor(registry, promhttp.HandlerOpts{})替代默认promhttp.Handler() - 确认 Go runtime 指标(如
go_gc_duration_seconds)是否出现在/metrics中——如果也没有,说明prometheus.MustRegister()没生效或注册时机太晚
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










