必须用 promhttp.handler() 暴露指标,因其严格校验 openmetrics 格式;counter 用于累计量(如 http_requests_total),gauge 用于瞬时值(如 go_goroutines);加 label 要避高基数和敏感字段;运行时指标需显式注册 newgocollector() 到同一 registry。

直接用 prometheus/client_golang 暴露指标,别自己写计数器或拼 metrics 文本——这是唯一靠谱的入门路径。
为什么必须用 promhttp.Handler() 而不是自己 http.HandleFunc?
因为 Prometheus 不认你手写的字符串。它严格校验 OpenMetrics 文本格式(比如必须有 # TYPE 行、标签键值要转义、末尾不能多空行)。自己拼错一个冒号或换行,prometheus server 就报 text format parsing error,抓不到数据。
- ✅ 正确做法:
http.Handle("/metrics", promhttp.Handler()) - ❌ 错误做法:
http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "# TYPE...") }) - 注意:如果用了自定义
prometheus.NewRegistry(),得配promhttp.HandlerFor(reg, ...),否则默认 registry 里没你的指标,返回空页或 500
Counter 和 Gauge 到底该用哪个?
语义错了,PromQL 就废了。rate(http_requests_total[5m]) 对 Gauge 永远是 0 或负数;而用 Gauge.Set() 记请求总数,服务一重启,数值归零,rate() 直接崩。
-
Counter:只增不减。适合累计量——http_requests_total、errors_total -
Gauge:可增可减。适合瞬时值——go_goroutines、memory_usage_bytes - 别把
runtime.NumGoroutine()直接塞进Counter,它本来就是瞬时快照,该包成Gauge
怎么加 label 才不翻车?
label 不是越多越好。高基数(比如 user_id、request_id)会让 Prometheus 内存暴涨甚至 OOM;带斜杠或空格的路径不处理,会导致指标爆炸。
- 路径变量要标准化:
/user/123→/user/{id},用strings.ReplaceAll或路由中间件预处理 - 敏感字段别进 label:
email、token这类绝对禁止 - 高频打点别在循环里反复调
counterVec.WithLabelValues("GET", "200", "/api"),提前存好实例复用 - 定义
NewCounterVec时标签顺序必须和WithLabelValues参数顺序一致,错一位就 panic
运行时指标(GC、goroutine 数)怎么暴露?
Go 标准库的 runtime 和 debug 包能读,但不会自动变成 Prometheus 格式。你得显式注册采集器,且不能依赖默认 registry。
- 必须手动注册:
reg.MustRegister(prometheus.NewGoCollector())(含 goroutine、GC、内存等) - 必须绑定到同一 registry:
promhttp.HandlerFor(reg, ...),否则/metrics里看不到go_goroutines - 别在
/metricshandler 里实时调runtime.NumGoroutine(),它虽快,但违反 collector 线程安全契约;用NewGoCollector()就够了
最常被忽略的一点:所有 MustRegister() 必须发生在 http.ListenAndServe 之前,且指向同一个 registry 实例。注册晚了、注册错地方、或者多个包各自 init() 里抢着注册同名指标,都会导致 panic 或指标缺失——这类问题没法靠日志一眼看出,得盯住注册逻辑本身。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











