/metrics 返回空或404说明kratos未注册指标handler,常见原因包括未调用srv.handle("/metrics", promhttp.handler())、路由被中间件拦截或http服务未启动成功。

/metrics 返回空或 404,说明指标暴露链路根本没通,不是采集配置问题,而是 Kratos 侧没正确挂载 handler 或没注册指标。
为什么 /metrics 返回空或 404
这是最常见也最容易误判的起点。Prometheus 报 “target down” 或 “no data”,第一反应常是去查 Prometheus 的 scrape 配置,但 Kratos 默认压根不暴露 /metrics —— 它连 HTTP handler 都没注册。
-
srv.Handle("/metrics", promhttp.Handler())没调用,或者路径写成了/actuator/metrics(Spring 风格) - 用了
http.NewServer+http.WithMiddleware,却把/metrics路由塞进了中间件链,结果被鉴权/限流/trace 中间件拦截了 - HTTP Server 根本没启动成功,端口被占、日志没报错,但
curl localhost:8000/metrics直接 connection refused
为什么 /metrics 有响应但内容为空
能访问到页面,但 body 是空的,说明 handler 挂对了,但 Prometheus client 没拿到任何注册的指标 —— Kratos 不会自动埋点,所有指标都得你手动创建并上报。
-
metrics.DefaultMeter创建的 counter/histogram 没在业务逻辑里调用.Add()或.Record(),指标从未触发上报 - 反复在 hot path(比如每个 HTTP 请求里)调用
meters.NewCounter,导致新指标对象不断生成但旧对象没复用,Prometheus registry 实际只保留最后一次注册的引用 - 用的是自定义
metrics.Meter实例,但没把它传给promhttp.HandlerFor,或者传错了 registry
怎么确认指标是否真被注册并上报
别依赖 Grafana 或 Prometheus UI 先看,先在本地验证指标生命周期是否闭环。
- 启动服务后,立刻
curl http://localhost:8000/metrics,确认返回 HTTP 200 且 body 包含文本格式指标(如# HELP http_requests_total) - 在代码中显式调用一次指标上报,比如
counter.Add(context.Background(), 1),再 curl,看对应指标值是否从 0 变成 1 - 检查
metrics.DefaultMeter是否被正确注入:Kratos 的app.Run()会默认提供一个全局 meter,但如果你自己 new 了App并覆盖了WithMetrics,就得确保它底层用的是 Prometheus-compatible meter
容易被忽略的兼容性细节
Kratos 的 metrics 抽象和 Prometheus client_golang 不是开箱即用的胶水关系,几个参数和初始化顺序必须对齐。
-
promhttp.Handler()内部用的是默认 registry,如果你调用了prometheus.MustRegister(...),它才能被扫到;如果自己 new 了 registry,就得用promhttp.HandlerFor(registry, promhttp.HandlerOpts{}) - 引入的 Prometheus client 版本要和 Kratos 的
go.mod兼容,v1.14+ 的prometheus/client_golang已移除部分旧接口,MustRegister行为有变化 - 如果启用了 Kratos 的 tracing 中间件,注意它默认不打 metrics 日志,
/metrics路由绕过中间件是安全做法,别为了“统一链路”强行加 trace
真正卡住人的地方,往往不是 Prometheus 怎么配,而是 Kratos 侧指标从创建、复用、上报到暴露的整个链条里,漏掉了一个 .Add() 调用,或少了一行 srv.Handle() —— 这些动作都不带错误提示,静默失败。











