Go服务集成Prometheus必须显式调用prometheus.MustRegister()注册指标(如CounterVec),注册须在init()或main()开头完成,否则/metrics为空;自定义Registry需配promhttp.HandlerFor();运行时指标需手动注册NewGoCollector()。

直接暴露 /metrics 端点不等于完成性能指标采集——漏掉注册、标签错配、运行时重复创建指标,都会导致数据缺失或 panic。
prometheus.MustRegister() 必须在 init 或启动早期调用
指标对象(如 CounterVec、HistogramVec)不是“定义完就能用”,必须显式注册到 Prometheus 默认注册器,否则 /metrics 返回空或 404。
-
MustRegister()是硬性要求,不是可选;它会 panic 而不是返回 error,方便早期发现问题 - 别在 HTTP handler 里注册——每次请求都触发注册会 panic
- 如果用了自定义
prometheus.NewRegistry(),后续必须用promhttp.HandlerFor(registry, ...),不能继续用promhttp.Handler() - Go 运行时指标(goroutines、GC、内存)需手动注册:
prometheus.MustRegister(prometheus.NewGoCollector())
Gin/Echo 中间件打点要避免 label 值非法或爆炸
HTTP 指标带 method、path、status 标签很常见,但 path 若含动态参数(如 /user/:id),不归一化会导致 label 组合爆炸,内存泄漏。
- 路径应提前规整:把
/user/123→/user/{id},否则每条 ID 都生成新 label,指标 cardinality 暴增 -
WithLabelValues()传入的值不能为空字符串,否则 panic;状态码要用strconv.Itoa(w.WriteHeader)而非r.Header.Get("Status")(后者不可靠) - 延迟直方图(
HistogramVec)的Buckets要覆盖真实业务耗时,比如 API 大部分在 100–500ms,却设[0.01, 0.1, 1],P99 就失真
runtime 指标采集别依赖单次快照
runtime.NumGoroutine() 和 runtime.ReadMemStats() 返回的是瞬时值,直接上报一次没意义;协程泄漏、内存缓慢增长必须靠趋势判断。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 用
time.Ticker每 10–30 秒采集一次,推送到同一Gauge实例(不是新建) - 别在
http.HandleFunc("/metrics", ...)里调用ReadMemStats()——这会让每次抓取都触发 GC 扫描,增加延迟抖动 - 注意
MemStats.Alloc是当前堆分配量,TotalAlloc是累计分配量,监控泄漏看前者,看吞吐看后者
OpenTelemetry 导出指标到 Prometheus 要绕过 double-collect
OTel Go SDK 支持 prometheus.Exporter,但若同时启用了 prometheus.NewGoCollector() 和 OTel 的 runtime meter,会导致同一指标(如 goroutines)被采集两次,Prometheus 抓取时出现重复样本错误。
- 启用 OTel 时,禁用
prometheus.NewGoCollector(),改用 OTel 的runtime.WithMinimumReadMemStatsInterval(5 * time.Second)控制采集频率 - OTel 的
Counter和Histogram默认不兼容 Prometheus 直接抓取,必须配置prometheus.NewExporter()并挂载到独立路由(如/otel/metrics),或统一导出到 OTLP Collector 再转 Prometheus 格式 - label 名称要对齐 Prometheus 约定:用下划线分隔(
http_request_duration_seconds),而非 OTel 默认的 kebab-case(http-request-duration)
最易被忽略的点:所有指标名和 label key 必须全程小写+下划线,哪怕只混入一个大写字母或空格,MustRegister() 就会静默失败或 panic;而 label value 的合法性(如路径中含 /、{)则由 Prometheus 服务端校验,前端看不到报错,只表现为数据断连。


















