go应用暴露prometheus指标需同时完成注册和路由挂载,推荐用promauto初始化指标并显式挂载/metrics路径;http指标应避免高基数label如动态path,goroutine和内存指标需关注趋势而非瞬时值,命名须符合规范如http_requests_total。

Go 应用暴露 Prometheus 指标端点要配对注册和路由
不注册指标就无法被 Prometheus 抓取,不暴露 /metrics 路由就等于没开监控入口。这两个动作必须同时完成,缺一不可。
常见错误是只调用 prometheus.MustRegister() 却忘了在 HTTP mux 中挂载 handler;或者用了 promauto 自动注册,却把 http.Handle("/metrics", promhttp.Handler()) 写错路径(比如写成 /metric 或漏掉斜杠)。
- 推荐用
promauto初始化指标:它自动注册到默认 registry,省去手动MustRegister步骤 - 必须显式挂载:
http.Handle("/metrics", promhttp.Handler()),且路径要和 Prometheus 配置的scrape_configs.job.metrics_path一致(默认就是/metrics) - 若使用 Gin/Echo 等框架,不要直接用
http.Handle,而要用对应框架的中间件方式注册,例如 Gin 中:r.GET("/metrics", gin.WrapH(promhttp.Handler()))
HTTP 请求指标要按 method/path/status 分维度,但高基数 label 会崩掉 Prometheus
用 prometheus.NewCounterVec 或 promauto.NewCounterVec 定义指标时,[]string{"method","path","status"} 看似合理,但 path 若含用户 ID、UUID、时间戳等动态段,会导致 label 组合爆炸——一个请求一个新 series,几小时就能打爆 Prometheus 内存。
- 安全做法:对 path 做聚合,比如
/user/{id}→/user/:id,用 Gin 的gin.Param或 chi 的chi.URLParam提前提取并替换 - 更稳妥的是去掉
path,只保留method和status;业务级路径统计改用日志 + Loki 或链路追踪里的 tag - 避免用 query string、request body、header value 做 label,它们天然高基数
goroutine 数和内存使用这类系统指标不能只看瞬时值
runtime.NumGoroutine() 和 runtime.ReadMemStats() 返回的是快照,单次采集意义有限。真正有用的是变化趋势和异常突增。
- goroutine 数建议用
prometheus.NewGaugeFunc包一层函数,每 10 秒采样一次并上报,而不是在每次 HTTP 请求里调用——后者会放大抖动噪声 - 内存指标优先用
memstats.Alloc(已分配)和memstats.Sys(总内存占用),而非HeapInuse等内部细节字段,后者语义不稳定且易受 GC 影响 - 注意:Go 1.22+ 的
runtime/metrics包提供了更细粒度、零分配的指标读取方式,但需手动映射到 Prometheus 类型,不建议新手直接切过去
指标命名必须遵循 Prometheus 规范,否则 Grafana 查不到
Prometheus 对指标名有强约束:[a-zA-Z_:][a-zA-Z0-9_:]*,且推荐用 _total、_duration_seconds、_bytes 等后缀表达类型。违反规则会导致 exporter 拒绝注册,或 Grafana 查询时返回空结果。
- 错误示例:
http-requests(含短横线)、HTTPRequests(大驼峰)、requests(无单位/语义模糊) - 正确写法:
http_requests_total(counter)、http_request_duration_seconds(histogram)、go_goroutines(gauge) - 所有 label 名也必须小写下划线,比如
http_method,不能写成HTTPMethod或httpMethod
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











