默认echo prometheus中间件不统计状态码,因其仅暴露基础指标如请求总量和延迟,未在http_requests_total中添加status标签;需手动在中间件中于next(c)后读取c.response().status并用countervec按method、path、status打标,同时注意路径标准化以防基数爆炸。

为什么默认的 Echo Prometheus 中间件不统计状态码?
因为 echo.NewHTTPHandler 和官方 promhttp.Handler 都只暴露基础指标(如请求总量、延迟),echo-prometheus 这类社区中间件默认也只打点 http_requests_total,但没按 status 标签分组。你看到的指标里没有 {status="404"} 这种形式,不是漏装,是压根没加这个标签逻辑。
怎么让 http_requests_total 按状态码自动打标?
必须手动在中间件中捕获响应状态码,并用 prometheus.CounterVec 带 status 标签记录。不能依赖 echo-prometheus 的开箱即用版本——它不支持。
- 定义指标时显式声明
status为 label:var httpRequestCounter = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total HTTP requests.", }, []string{"method", "path", "status"}, ) - 注册到 Prometheus:
prometheus.MustRegister(httpRequestCounter)
- 写中间件,在
c.Response().Status可读后打点(注意:必须在next(c)之后):func MetricsMiddleware() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { err := next(c) status := c.Response().Status if status == 0 { status = 200 // echo 默认未写状态时 Response.Status 为 0 } httpRequestCounter.WithLabelValues( c.Request().Method, c.Path(), strconv.Itoa(status), ).Inc() return err } } }
为什么 c.Response().Status 在 next(c) 前是 0?
因为 Echo 的响应体和状态码是在 handler 执行完、返回 error 后才真正写入 http.ResponseWriter。中间件里提前读 c.Response().Status 就是未初始化值。常见错误是把打点逻辑放 next(c) 前,结果所有请求都记成 status="0",再查 Grafana 也看不到真实分布。
- 别用
c.Response().StatusCode()—— 这个方法不存在,会编译失败 - 别依赖
echo.HTTPError的Code字段:自定义错误或 panic 恢复中间件可能绕过它 - 如果用了
echo.HTTPErrorHandler统一处理错误,确保它调用了c.Logger().Error()但没提前写状态码;否则中间件仍可能读到旧值
路径分组要不要做?比如把 /users/123 归为 /users/:id?
要,但得在打点前做路径标准化,否则 /users/123 和 /users/456 会生成上百个不同 label,撑爆 Prometheus 内存(cardinality 爆炸)。Echo 的 c.Path() 返回的是注册路由的原始 pattern,但前提是你的路由是用 e.GET("/users/:id", ...) 注册的。
- 直接用
c.Path()是安全的——它返回的就是 pattern,不是实际 URL 路径 - 如果你用了正则路由或手动解析路径,就得自己映射回 pattern,否则指标不可聚合
- 避免把 query 参数(如
?page=2)塞进pathlabel:Prometheus 不适合高基数 label,query 应该丢弃或单独提取关键参数(如page)作为独立 label
状态码打标本身不难,难的是 label 设计别踩 cardinality 坑,还有时机——所有中间件都得等 response 真正落盘后再读状态。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











