prometheus客户端应直接使用prometheus/client_golang库注册指标,所有指标变量须为包级变量并用mustregister注册;http延迟用histogramvec而非summary;goroutine数等动态值必须用gaugefunc实现懒计算;模块需提供可配置的metricshandler避免端口与路由冲突。

用 prometheus 客户端直接注册指标,别自己造轮子
Go 生态里最成熟、最轻量的监控埋点方案就是 prometheus/client_golang。它不依赖外部服务,只负责采集和暴露指标,和 Go 的 http.ServeMux 或 net/http 天然契合。别写自定义计数器或时间统计结构体——prometheus.NewCounter、prometheus.NewHistogram 这些工厂函数已经覆盖绝大多数场景。
常见错误是把指标变量声明在函数内部,导致每次调用都新建一个指标实例,造成内存泄漏和指标重复注册报错 duplicate metrics collector registration attempted。
- 所有指标变量必须声明为包级变量(通常放在
metrics.go文件里) - 用
prometheus.MustRegister()注册,它会在注册失败时 panic,比静默忽略更早暴露问题 - 如果模块要支持热加载或多次初始化,改用
prometheus.Register()并捕获 error 判断是否已注册
HTTP 请求延迟用 HistogramVec,别用 Summary
Summary 看似简单,但它在 Prometheus 服务端无法聚合(比如跨实例求 P95),且客户端需维护滑动窗口,对高 QPS 服务有 GC 压力。而 HistogramVec 把分位计算交给 Prometheus Server,更可靠也更省资源。
典型用法是按路由或 handler 名打标签:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
var httpDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests.",
Buckets: prometheus.ExponentialBuckets(0.001, 2, 10), // 1ms ~ 1s
},
[]string{"handler", "status_code"},
)
- 标签名用小写字母+下划线,避免 Prometheus 查询语法报错
-
Buckets别用默认值——默认从 100ns 开始,对 Web 服务太细,浪费存储;建议从 1ms 起,按 2 倍指数增长到 1s 或 2s - 在 middleware 中用
httpDuration.WithLabelValues(r.URL.Path, strconv.Itoa(w.Status())).Observe(elapsed.Seconds())记录耗时
goroutine 数量监控必须用 prometheus.NewGaugeFunc
很多人想手动 runtime.NumGoroutine() + Gauge,但漏掉关键点:goroutine 数量是瞬时值,不能靠主动调用更新——它必须被 Prometheus Server 拉取时实时计算,否则会 stale。
正确做法是用 GaugeFunc 注册一个“懒计算”指标:
prometheus.MustRegister(prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
Name: "go_goroutines_total",
Help: "Number of goroutines currently running.",
},
func() float64 { return float64(runtime.NumGoroutine()) },
))
- 这个函数会在每次 Prometheus 抓取时执行,保证数值绝对新鲜
- 别在 init 函数里调用
runtime.NumGoroutine()赋值给普通Gauge——那只是快照,之后永远不变 - 同理适用于内存使用量(
runtime.ReadMemStats)、连接数等动态值
模块独立暴露指标端点,避免和主服务冲突
如果你的 Go 模块是可插拔的 SDK 或中间件(比如日志模块、DB 封装层),不要硬编码绑定到 http.DefaultServeMux。主服务可能用的是 gin.Engine、echo.Echo 或自定义 mux,直接 http.Handle("/metrics", promhttp.Handler()) 会 panic 或被覆盖。
- 提供一个函数返回
http.Handler,由使用者决定挂载路径和 mux:func MetricsHandler() http.Handler - 如果模块内部启动了独立 HTTP server(如 sidecar 场景),监听地址必须可配置,默认用
localhost:9091避免端口冲突 - 暴露前检查
os.Getenv("DISABLE_METRICS") == "true",方便测试或禁用场景快速关闭
埋点不是加完就完事——指标命名是否一致、标签维度是否正交、采样频率是否合理,这些得靠后续看 Grafana 面板时反推。一开始宁愿少几个指标,也要确保每个都能被查、能被聚合、能真实反映模块行为。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










