go语言需显式注册prometheus.newgocollector()并挂载/metrics端点才能暴露go_goroutines指标,否则仅显示空行或基础进程指标;注册须在http.listenandserve()前完成,且handler路径必须严格匹配。

Go语言暴露goroutine指标不需要手动埋点,prometheus.NewGoCollector()注册后自动输出go_goroutines,但必须确保它被正确加载到默认注册器,并且/metrics端点已挂载——漏掉任一环节都会导致指标不出现。
必须调用prometheus.MustRegister(prometheus.NewGoCollector())
Go运行时指标(包括go_goroutines)不是默认开启的。即使你引入了client_golang,不显式注册NewGoCollector,/metrics里就只有一堆空行或仅含process_*这类基础进程指标。
- 注册位置必须在
http.ListenAndServe()之前,推荐放在func init()或main()开头 - 不要在HTTP handler里重复调用,否则会panic:同名collector只能注册一次
- 若用了自定义
prometheus.Registry,得用promhttp.HandlerFor(reg, nil)绑定,否则promhttp.Handler()仍从默认注册器读,看不到你的goroutine指标
/metrics端点返回空或404?先查handler是否挂载
常见错觉是“我已经注册了collector,指标应该有了”,结果curl http://localhost:8080/metrics返回空或404——这和指标注册完全无关,纯粹是HTTP路由没通。
- 原生
net/http:必须写http.Handle("/metrics", promhttp.Handler()),且这行要在http.ListenAndServe()之前 - Gin框架:不能用
router.GET("/metrics", gin.WrapH(...))(v1.9+有Content-Type协商bug),改用router.GET("/metrics", func(c *gin.Context) { promhttp.Handler().ServeHTTP(c.Writer, c.Request) }) - 路径必须严格匹配:Prometheus配置里的
scrape_configs.metrics_path默认是/metrics,你写成/metric或metrics(缺斜杠)都抓不到
go_goroutines值跳变大?别只盯瞬时数字
go_goroutines是runtime.NumGoroutine()的快照,单次采集毫无意义。真正要关注的是趋势和突增:
- 持续缓慢上涨 → 可能存在goroutine泄漏(比如channel阻塞、timer未stop、defer未执行)
- 周期性尖峰 → 检查是否在请求中启动了未回收的goroutine(如忘记
defer wg.Done()) - 别用它做告警阈值硬判断,建议配合
rate(go_goroutines[5m]) > 0看增长速率,或用go_goroutines > bool 1000+absent_over_time(go_goroutines[1h])排除毛刺 - 如果想降低采集开销,可用
prometheus.NewGaugeFunc包装,每10秒采样一次,而不是每次抓取都调NumGoroutine()
goroutine指标本身很简单,但它的价值完全依赖于注册、暴露、采集三者的对齐;最容易被忽略的是handler挂载顺序和注册器绑定一致性——这两处出问题,go_goroutines永远躺在代码里,不会出现在Prometheus里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











