promhttp.Handler() 默认只暴露已注册指标,未调用 prometheus.MustRegister() 的业务指标不会出现在 /metrics 中;运行时指标如 go_goroutines 也需显式注册 NewGoCollector(),且注册须在 http.ListenAndServe() 之前完成。

promhttp.Handler() 默认只暴露已注册的指标,不注册就看不到任何业务数据——哪怕你写了 http_requests_total 变量,没调 MustRegister(),它在 /metrics 里就是空的。
为什么 /metrics 返回 200 却什么指标都没有?
这不是网络或配置问题,是注册环节漏掉了。Prometheus 不扫描变量,只读注册器里“活”的指标。
-
go_goroutines、go_memstats_alloc_bytes这类运行时指标默认不注册,必须显式加:prometheus.MustRegister(prometheus.NewGoCollector()) - 自定义
CounterVec或Gauge必须在init()或main()开头注册,不能在 handler 里反复 new + register,否则 panic 报duplicate metrics collector registration attempted - 用
prometheus.NewRegistry()自定义注册器后,promhttp.Handler()就查不到它——得换promhttp.HandlerFor(reg, promhttp.HandlerOpts{}) - 注册顺序必须在
http.ListenAndServe()之前,否则 handler 启动时注册器还是空的
用 NewCounterVec 还是 NewCounter?
99% 的 HTTP 监控场景该用 NewCounterVec,裸 NewCounter 基本等于白埋。
-
NewCounter没 label,所有请求都堆在一个时间序列里,rate(http_requests_total[5m])算出来是个笼统数字,没法按method或status下钻 -
NewCounterVec定义时要传[]string{"method", "endpoint", "status"},调用时必须严格按顺序、非空、类型一致地传值:.WithLabelValues("GET", "/api/users", "200").Inc() - 传空字符串(
"")、nil、少一个值、顺序错,指标会静默丢失——/metrics里查不到,也不报错 - 路径中带 ID(如
/user/123)要泛化成/user/{id},否则 label 基数爆炸,Prometheus 内存飙升
为什么 Prometheus 显示 Target UP 却查不到 http_requests_total?
90% 是网络和监听配置问题,跟 Go 代码无关。先在 Prometheus 所在机器上跑这句:
curl -v http://your-go-app:9091/metrics
- 如果连不上:检查 Go 服务监听地址是不是
127.0.0.1:9091?改成:9091(即绑定""),否则外部无法访问 - 如果是 Docker 部署:
scrape_configs.targets别写localhost:9091,容器内localhost指自己;Mac/Win 用host.docker.internal:9091,Linux 用宿主机真实局域网 IP - 如果返回 404 或内容为空:确认是否把
/metrics挂在了业务 mux 上(比如 Gin 的r.GET("/metrics", ...))?业务中间件可能篡改Content-Type,导致 Prometheus 拒绝解析——必须用独立http.Server,只挂promhttp.Handler() - 如果返回
text/html:说明有中间件注入了 HTML 头,promhttp.Handler()要求text/plain; charset=utf-8
HTTP 请求耗时该用 Histogram 还是 Summary?
选 Histogram,别碰 Summary —— 它在服务重启后分位数重置,P95 查询会断崖式跳变。
立即学习“go语言免费学习笔记(深入)”;
-
Histogram自动生成三组指标:_sum、_count、_bucket,支持稳定查询http_request_duration_seconds{quantile="0.95"} -
Buckets别用默认DefBuckets硬套,Web API 延迟建议设为:[]float64{0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10} - 必须带
Labels,至少含method和status_code,否则无法定位慢请求来源 - 埋点必须在 handler 执行完、状态码已写入后调用
.Observe();用中间件封装时,要包装ResponseWriter拿到真实 status,不能靠defer猜
WithLabelValues() 的字符串不能含空格、换行、控制字符,且必须在请求路径解析完成后做脱敏(比如 chi.URLParam(r, "id") 替换为 "{id}"),晚一步,指标基数就失控。


















