用中间件自动埋点可避免修改handler,gin等框架天然支持:需用time.since()+defer算延迟、包装responsewriter取状态码、标签仅保留method/status/handler三类,注册指标须用独立registry并显式注册,严控cardinality防oom。

用中间件自动埋点,别改 handler 函数
可观测指标加得越靠近请求入口,业务代码就越干净。Gin、Echo、yokai 这类框架都支持中间件,是埋点的天然位置——http.Request 和 http.ResponseWriter 都在手边,延迟、状态码、方法名全可捕获,完全不用碰业务 handler 里的逻辑。
关键点有三个:
- 延迟必须用
time.Since()+defer计算,否则可能漏掉 panic 或 early return 的路径 - 状态码要从
ResponseWriter的包装体里取(比如responseWriter.Status()),不能只看 handler 返回值 - 标签(label)只保留 method、status、handler(如
/api/users),别塞user_id或完整路径
注册指标前先创建独立 Registry,别依赖 DefaultRegisterer
promhttp.Handler() 默认只暴露 go_* 指标,你自定义的 CounterVec 或 HistogramVec 不会自动出现——这是 /metrics 返回空或只有运行时指标的最常见原因。
正确做法是显式创建新注册表,并把指标注册进去:
reg := prometheus.NewRegistry()
reqCounter := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total HTTP requests",
},
[]string{"method", "status", "handler"},
)
reg.MustRegister(reqCounter) // 必须调用
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
注意:MustRegister() 会在注册失败时 panic,适合启动期;若需容忍重复注册,用 reg.Register() + 错误检查。
标签维度要收口,别让 cardinality 爆炸
一个 user_id="123456789" 标签,每新增一个用户就多一条时间序列;几万用户跑几天,Prometheus 就可能 OOM。这不是理论风险,是真实踩过的坑。
实际操作中守住三条线:
- 路径标签只用路由模板(
/api/v1/users/:id),不用r.URL.Path - 错误详情不进 label,用单独的
error_count指标 + 日志记录 full error message - 高频变动字段(如 request_id、trace_id)绝对不放 label,它们只该出现在日志和 trace 中
用 yokai 或 otel-auto-instrumentation 降低侵入性
如果连中间件都不想写,有两个轻量级选择:
- yokai 框架自带可观测模块,只需在
fx.New()里引入fxhttpserver.FxHTTPServerModule,它会自动注册基础指标和健康检查端点 - 阿里云开源的
otel二进制工具,直接替换go build:执行./otel go build .,生成的二进制就能自动上报 HTTP、DB、RPC 指标和 trace,零代码修改
这两种方式都绕开了手动注册指标的时机判断和标签设计,适合快速落地,但代价是灵活性下降——比如无法按业务语义定制 handler 标签,只能接受默认聚合粒度。
真正难的不是“怎么加指标”,而是“加哪些标签”和“什么时候注册”。前者决定 Prometheus 能否查出问题,后者决定指标是否真的被采集到。这两处一旦出错,后续所有 Grafana 图表和告警都是空中楼阁。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











