最简单方式是用 promhttp.handler() 暴露 /metrics 端点,它自动处理内容协商、编码和压缩;必须先调用 prometheus.mustregister() 注册指标,否则无法采集。

用 promhttp.Handler() 暴露指标端点最简单
Prometheus Exporter 本质就是一个 HTTP 服务,只负责在特定路径(通常是 /metrics)返回符合格式的指标文本。Go 生态里最直接的方式就是用官方客户端库的 promhttp.Handler() ——它自动处理内容协商、编码、gzip 压缩和基本头部,不用自己拼字符串。
常见错误是手动写 http.HandleFunc("/metrics", ...) 然后用 fmt.Fprintf 输出指标,结果遇到类型不匹配、换行符错乱、Content-Type 缺失等问题,被 Prometheus 抓取时直接报 text format parsing error。
- 必须注册
prometheus.MustRegister()后才能被promhttp.Handler()采集到 - 不要在 handler 里调用
promhttp.HandlerFor()并传入自定义 registry,除非你明确要隔离指标集 - 默认监听
http.DefaultServeMux,如果用了 Gin/echo 等框架,得用promhttp.Handler().ServeHTTP(w, r)显式转发
自定义指标要用 prometheus.NewGaugeVec() 或 NewCounterVec()
硬编码单个 Gauge 只能表达一个值,实际场景中往往需要按标签维度区分,比如不同实例、不同接口、不同状态码。这时候必须用 Vec 类型,它底层是带标签的指标集合,取值前必须用 WithLabelValues() 填满所有 label。
典型翻车现场:定义了 requestsTotal = prometheus.NewCounterVec(..., []string{"method", "status"}),但调用 requestsTotal.WithLabelValues("GET") 少传了一个 status,运行时报 panic:expected 2 label values but got 1。
- label 名称用小写字母+下划线,避免大写或中划线(Prometheus 查询语法对大小写敏感,且某些 exporter 工具会自动转小写)
- 不要在循环里反复调用
WithLabelValues()创建新指标对象——它内部有 map 查找,高频调用会拖慢性能;应缓存结果变量 - 如果 label 值来自用户输入(如 URL 路径),需先做白名单过滤或哈希脱敏,否则可能因 label 值过多导致内存暴涨
暴露业务指标前先确认 prometheus.Gatherers 是否被正确注入
有些项目把指标注册分散在多个包里,或者用依赖注入容器管理 registry,结果主程序启动时漏掉了某部分指标注册,Prometheus 抓到的 /metrics 里压根没有那些数据,但日志又没报错,排查起来特别耗时。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
根本原因在于:promhttp.Handler() 默认只从 prometheus.DefaultRegisterer(即全局 registry)收集指标。如果你用了自定义 registry(比如为了单元测试隔离),就必须显式传给 handler。
- 启动时加一行日志:
log.Printf("registered metrics: %v", prometheus.DefaultGatherer.Gather()),快速验证是否有指标产出(注意别在生产环境频繁调用Gather()) - 多 registry 场景下,用
prometheus.Gatherers{reg1, reg2}合并后再传给promhttp.HandlerFor(...) - 避免在 init() 函数里注册指标,因为 import 顺序不可控,可能导致部分 registry 还没初始化就被引用
调试时 curl /metrics 看原始输出比看日志更有效
很多开发者习惯盯着服务日志等 “started”、“listening on :9100”,但 Prometheus 抓不到数据时,真正该第一时间检查的是 curl -v http://localhost:9100/metrics 的响应体和状态码。90% 的问题在这里就能定位。
典型异常反馈:
- 返回空内容或 404 → 路由没注册,或 mux 被覆盖
- 返回
text/plain; version=0.0.4; charset=utf-8但内容不是指标格式 → 手动写了 handler 但没遵循 exposition format - 返回 500 + panic trace → 某个指标的
Collect()方法里 panic(比如除零、nil deference) - 返回正常但 Prometheus UI 显示
no data→ 检查 target 配置里的scrape_interval和 exporter 启动时间是否差太多,或者防火墙拦截了抓取请求
复杂点永远在 label 组合爆炸、指标生命周期管理、以及 registry 注入时机——这些地方不报错,但数据就是不对。盯住 /metrics 的原始输出,比翻文档快得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










