会,因为默认绑定全局注册器prometheus.defaultregisterer,所有经mustregister()注册的指标(含go_、process_等)均被导出;生产环境应创建独立注册器、仅注册业务指标,并手动添加所需运行时指标。

直接用 promhttp.Handler() 会暴露所有指标?
会,而且是默认行为——它绑定的是全局注册器 prometheus.DefaultRegisterer,只要调用过 prometheus.MustRegister() 的指标(包括 go_*、process_* 等 SDK 自带指标)全都会被吐出来。这不是 bug,是设计使然。
生产环境必须隔离:只暴露业务指标,屏蔽运行时噪音。否则 Prometheus 抓到的指标里混着几百个无关的 go_goroutines 或 process_cpu_seconds_total,查问题时根本找不到重点。
- 显式创建独立注册器:
reg := prometheus.NewRegistry() - 只注册你要的指标:
reg.MustRegister(httpDuration),别碰prometheus.MustRegister() - 如需运行时指标,手动加:
reg.MustRegister(prometheus.NewGoCollector()),而不是依赖默认行为 - 启动 handler 时传入该注册器:
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
HTTP 请求指标为什么总为空或状态码全是 0?
promhttp.Handler() 只负责暴露 /metrics 端点,它不拦截你的业务请求,自然不会记录响应时间、状态码、路径这些信息。空指标或 http_request_duration_seconds 分位数恒为 0,说明你没在请求链路里真正打点。
必须写中间件,在 ServeHTTP 前后抓取耗时和状态。别用已标记 deprecated 的 InstrumentHandler 系列函数,新项目应自己封装或用 promhttp.NewHandler() 配合自定义 handler。
- 计时必须显式:用
start := time.Now()+defer调用.Observe(time.Since(start).Seconds()) - 状态码必须是字符串,比如
"200",不是200;panic 后没执行 defer 就会漏打点 - 标签值不能为空:
c.FullPath()比c.Request.URL.Path更安全(Gin 场景),能拿到路由模板如/api/v1/users/:id,避免高基数 - 别在 handler 里重复注册指标,否则 panic:
duplicate metrics collector registration attempted
NewCounterVec 和 NewGaugeVec 标签怎么设才不炸?
标签名非法、值为空、动态注入用户输入,三者任一都会导致 panic 或 cardinality 爆炸。比如用 user_id 当 label,10 万用户就生成 10 万个时间序列,Prometheus 内存直接拉满。
业务指标必须向量化,但得守规矩:标签是维度,不是日志字段。
- 标签名只能是小写字母、数字、下划线,不能以数字开头,不能含短横线(
pay-channel→pay_channel) - 静态属性(如
version、instance)可用ConstLabels;动态值(如请求路径、状态)必须走.WithLabelValues() - 高频更新场景缓存
prometheus.Labels或子指标对象,避免每次调用都做 map 查找 - 校验输入:
if channel == "" || status == "" { return },别让空字符串进指标系统 - 聚合代替细分:需要“按用户统计”时,改用“按渠道+状态聚合”,再配合日志查具体 ID
Exporter 启动后 /metrics 返回 404 或空内容?
最常见原因是 HTTP 路由挂载错位:要么 http.Handle() 调得太晚(server 已启动),要么用了自定义 http.ServeMux 却没传给 http.Server 实例。
Go 默认复用全局 http.DefaultServeMux,但一旦你显式 new 了一个 mux,就必须把它塞进 http.Server{Handler: myMux},否则 http.Handle() 仍往默认 mux 上注册,而 server 用的却是你那个空 mux。
- 确认 handler 是否真被挂到生效的 mux 上:打印
http.DefaultServeMux的路由表(调试时临时加) - 检查
http.ListenAndServe()是否传了 nil —— 这会让 server 用默认 mux;若用了自定义 mux,必须显式传入 - 确保
/metrics路径没被其他中间件拦截或重写(比如 Gin 的Use()顺序不对) - Content-Type 必须是
text/plain; version=0.0.4,浏览器直接访问可能显示空白,用curl -H "Accept: text/plain" http://localhost:8080/metrics验证
c.FullPath() 在 Gin 中必须在中间件里取,放到 handler 里可能已被重写;WithLabelValues() 的参数顺序一旦和初始化时的 []string{"path","method","status"} 对不上,就会 panic,且错误信息极难定位。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











