Go微服务监控告警必须围绕SLA与故障定位闭环设计:HTTP指标用CounterVec按method/path/status下钻,避免label爆炸;耗时用HistogramVec配SLA桶,Observe传秒级浮点数;运行时指标需告警goroutine泄漏;错误率告警须加分母守卫,for至少2m,指标注册须早于server启动。

Go 微服务的监控告警指标不是“能采集就行”,而是必须围绕 SLA 和故障定位闭环设计——暴露错、聚合错、告警错,三者任一出问题,告警就失去意义。
HTTP 请求类指标:用 CounterVec 而不是全局变量
直接在 handler 里写 reqCount++ 并发下会 panic,且无法按 method/path/status 维度下钻。必须用 prometheus.NewCounterVec 初始化一次,注册后复用。
- label 名必须是合法标识符:用
method、path、status,别用/user/:id这种含斜杠的原始路径;应标准化为/user/{id} - 避免 label 组合爆炸:不要把
user_id当 label,它会让时间序列数量失控;用户维度聚合应在 Grafana 或 PromQL 中做 - 记录时机要可靠:在中间件中 defer 执行
counterVec.WithLabelValues(...).Inc(),确保即使 panic 也记一次成功/失败
耗时类指标:HistogramVec 是唯一合理选择
SLA 要求的是 P95/P99 延迟是否达标,Summary 不支持 rate() 和跨时间窗口分位数计算,查出来就是错的;Gauge 只存瞬时值,毫无意义。
- Buckets 必须覆盖 SLA 目标:若承诺 200ms 内响应,
buckets至少设为[]float64{0.05, 0.1, 0.2, 0.5},否则histogram_quantile(0.95, ...)算不出真实值 - Observe() 必须传秒单位浮点数:
time.Since(start).Seconds(),别传毫秒或整数,否则直方图桶对不上 - 别在 handler 外部调用
Observe():比如异步任务完成后再记耗时,会导致时间戳错乱、分位数失真
运行时指标:别漏掉 goroutines 和内存泄漏信号
Go 的 runtime.NumGoroutine() 和 go_memstats_heap_inuse_bytes 是免费的“健康快照”,但很多人只暴露不告警。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
-
go_goroutines持续上涨是 goroutine 泄露强信号:告警表达式建议写成go_goroutines > 1000 and (go_goroutines offset 5m) ,排除启动期尖峰 -
go_memstats_heap_inuse_bytes突增可能预示内存泄漏,但需结合rate(go_gc_duration_seconds_sum[5m])判断:如果 GC 耗时也在涨,大概率是对象没释放 - 别依赖
expvar:它不兼容 Prometheus 格式,promhttp.Handler()不识别,抓取结果为空
告警规则里 rate() 的坑:永远配 for + 守卫条件
写 rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05 会误报——刚启动时分母为 0,除零导致 NaN,Prometheus 默认当 0 处理,结果永远不触发。
- 必须加守卫:改写为
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05 and rate(http_requests_total[5m]) > 0.1 -
for:不是可选项:瞬时抖动太常见,for: 2m是底线,for: 5m更稳妥 - 别用
count_over_time()做告警:它不随样本数线性增长,长窗口(如[1h])会拖慢 Prometheus 查询引擎,影响其他规则执行
最容易被忽略的是指标生命周期——注册必须早于 HTTP server 启动,MustRegister() 在 init() 里最安全;而告警规则里的 PromQL 表达式,没人验证它是否真能返回非空结果,直到故障发生那天才暴露。

















