expvar.handler()不能被prometheus直接抓取,因其返回json格式而prometheus仅支持文本格式指标;需通过中间层转换(如victoriametrics/metrics)或改用原生prometheus指标。

expvar.Handler() 为什么不能直接被 Prometheus 抓取
因为 expvar.Handler() 返回的是 JSON 格式,而 Prometheus 只认文本格式的指标(如 # HELP http_requests_total Total number of HTTP requests. 开头的纯文本)。直接把 /debug/vars 挂到 /metrics 下,Prometheus 抓到的就是一堆 JSON,解析失败,抓取状态变成 DOWN。
常见错误现象:curl http://localhost:8080/metrics 返回 JSON 内容,但 Prometheus UI 的 Targets 页面显示 “invalid metric name” 或 “expected float, got ‘{’”。
- ExpVar 是 Go 标准库机制,设计目标是调试和轻量观测,不是监控协议适配
- Prometheus 要求指标名合法(小写字母/数字/下划线)、标签键值对明确、类型声明清晰,JSON 无法满足
-
expvar.NewMap("db_pool")这类嵌套结构在 JSON 里是对象,Prometheus 无法展开成扁平指标
如何把 expvar 指标转成 Prometheus 格式
必须用中间层转换,不能靠手动拼字符串或重写 Handler。推荐两种方式:
- 用
github.com/prometheus/client_golang/expfmt+ 自定义 exporter:读取/debug/vars响应,解析 JSON,映射为prometheus.Metric实例,再注册到自定义prometheus.Registerer - 更常用的是直接弃用 expvar,改用
prometheus.NewCounter等原生指标 —— 因为expvar.Float的Add()非原子,高并发下数值漂移;expvar.Int虽原子,但不支持标签维度,没法做{method="POST",endpoint="/api/v1/user"}这种切片 - 若已有大量 expvar 代码(如 BadgerDB 内置指标),可用
github.com/VictoriaMetrics/metrics替代:它兼容 expvar 接口,又原生输出 Prometheus 文本格式,且性能更好
暴露 /debug/vars 时最容易忽略的三件事
即使只用 expvar 做本地调试,也常因配置疏漏导致指标不可见:
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
-
http.Handle("/debug/vars", expvar.Handler())必须在http.ListenAndServe()之前调用;否则服务已启动,路由未注册,返回 404 - 如果用了自定义
http.ServeMux(比如加了日志中间件),不能依赖http.DefaultServeMux,得显式把 handler 挂到你的 mux 上:mux.Handle("/debug/vars", expvar.Handler()) -
expvar.Publish("my_counter", myCounter)必须执行一次,变量才真正进入全局注册表;仅expvar.NewInt("my_counter")不会出现在 JSON 中
生产环境千万别直接暴露 /debug/vars
它默认带 cmdline、memstats、goroutines 等敏感字段,没鉴权、无 TLS、路径固定,公网暴露等于交出进程快照。
正确做法:expvar.Unregister("cmdline") 和 expvar.Unregister("memstats") 移除高风险项,再把 handler 挂到非公开路径(如 /internal/metrics),并加 Basic Auth 中间件。但更稳妥的方案是:用 promhttp.Handler() 暴露 /metrics,只放业务需要的指标,其余全过滤。
复杂点在于指标语义对齐——比如 expvar 里的 heap_alloc 对应 Prometheus 的 go_memstats_heap_alloc_bytes,字段名、单位、采集频率都不一致,直接转换容易误判内存泄漏。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










