业务指标在/metrics可见但prometheus抓不到,八成因未注册进defaultregisterer:自定义指标需显式调用prometheus.mustregister(),否则静默忽略;handler内注册不可靠,同名指标重复注册会panic,高基数label须严控,多实例需注入唯一instance标签并确保relabel配置正确重写__address__。
业务指标在 /metrics 里能看到,但 prometheus 抓不到——八成是指标没注册进 defaultregisterer,而不是配置或网络问题。
为什么 promhttp.Handler() 返回数据却查不到自定义指标
默认的 promhttp.Handler() 只暴露 prometheus.DefaultRegisterer 中的指标。Go 运行时指标(如 go_goroutines、process_cpu_seconds_total)是自动注册的;但你用 prometheus.NewCounter() 或 promauto.NewCounterVec() 创建的指标,必须显式调用 prometheus.MustRegister() 才会生效。
- 漏掉
MustRegister()→ 指标完全不出现,/metrics里没有,Prometheus 也抓不到(静默忽略,无报错) - 在 HTTP handler 里注册 → 第一次抓取时指标尚未存在,后续抓取才可能看到,但时间线错乱,不可靠
- 多个包都注册同名指标(如都用
promauto.NewCounterVec(..., "http_requests_total"))→ 启动 panic:duplicate metrics collector registration attempted
高频长连接场景下必须用 CounterVec,但 label 基数要严控
WebSocket/gRPC 流等长连接服务,请求维度天然丰富(method、path、status、client_ip、tenant_id…),直接用裸 Counter 无法区分维度;但滥用 label 会导致 series 爆炸,/metrics 渲染变慢甚至超时(>10k series 就危险)。
- 路径、用户 ID、订单号、原始 URL 等高基数字段绝不能当 label 值
- 应聚合后打点:比如把
/api/v1/users/123归一为/api/v1/users/{id},再设为 label - 单个
CounterVec的 label 组合总数建议控制在 1k 以内;超限时考虑拆分指标或改用Summary -
CounterVec必须在init()或启动早期创建并注册一次,禁止在 handler 里反复调用NewCounterVec()
滚动发布/多实例部署时如何避免指标混淆
所有实例共用 DefaultRegisterer,Prometheus 抓到的是混合态指标(比如 http_requests_total{method="GET"} 是三台机器加总),根本无法下钻到单实例粒度。
- 必须给每个实例注入唯一标识:通过环境变量(如
POD_NAME)、启动参数或配置文件传入instance标签值 - 在注册指标时统一附加该标签:
WithLabelValues("GET", "200", os.Getenv("POD_NAME")) - 更稳妥的做法是使用
prometheus.NewRegistry()替代全局注册器,并把instance作为 registry 的一部分隔离(需配合自定义promhttp.HandlerFor()) - Prometheus 配置中也要确保
job和instance标签能对齐,否则 relabel 会丢目标
动态抓取失败?先确认 __address__ 是否被正确重写
在 Kubernetes 或 Consul 等动态发现场景下,relabel_configs 不是给指标加标签,而是改 target 本身的抓取地址。常见错误是只改了 instance 标签,却忘了重写 __address__,结果还是连到默认端口或空地址。
- 必须显式设置
target_label: __address__,且replacement要合法(如$1:$2,不能是http://$1) -
source_labels要包含所有参与拼接的字段,顺序和regex捕获组严格对应 - 用
action: keep过滤 target 时,注意它是“与”关系;一条不匹配,整个 target 就被丢弃,不会报错,容易误判 - 验证方法:访问 Prometheus 的
/targets页面,看实际生效的 target 地址和 labels 是否符合预期
最易被忽略的一点:长连接服务指标更新频率低(比如连接建立后几小时才发一次心跳),但 Prometheus 默认 15s 抓一次。如果指标值长时间不变,rate() 类函数会返回 0,不是采集失败,而是语义上“没变化”。需要结合 count_values() 或 changes() 判断活跃连接数是否归零。











