go接口统计必须在中间件层统一拦截请求生命周期,通过自定义responsewriter捕获状态码与耗时,gin中需将stats中间件置于最外层并标准化路径,避免超时和指标维度缺失导致统计失真。

Go 接口数据统计不能只靠日志打点,必须在中间件层统一拦截请求生命周期,否则指标缺失、时间不准、上下文丢失。
用 http.Handler 中间件捕获请求耗时与状态码
标准库的 http.ServeMux 不提供钩子,必须手动包装 http.Handler。核心是用闭包包裹原始 handler,在 ServeHTTP 前后记录开始时间、响应状态码和 body 大小。
- 别直接改
ResponseWriter的Write方法——它不负责写状态码,WriteHeader才是关键入口 - 必须用自定义
responseWriter类型实现http.ResponseWriter接口,重写WriteHeader和Write,否则 304/204 等无 body 响应会漏统计 - 注意并发安全:每个请求一个计数器实例,不要复用或共享
time.Time变量
type statsWriter struct {
http.ResponseWriter
statusCode int
wroteHeader bool
}
func (w *statsWriter) WriteHeader(code int) {
w.statusCode = code
w.wroteHeader = true
w.ResponseWriter.WriteHeader(code)
}
func (w *statsWriter) Write(b []byte) (int, error) {
if !w.wroteHeader {
w.statusCode = http.StatusOK
w.wroteHeader = true
}
return w.ResponseWriter.Write(b)
}
在 Gin 中使用 gin.HandlerFunc 统计并上报 Prometheus
Gin 的中间件本质是 gin.HandlerFunc,适合注入指标收集逻辑。Prometheus 客户端(prometheus/client_golang)要求指标注册到全局 Registerer,但注意避免重复注册。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用
prometheus.NewCounterVec按method、path、status多维打点,别用单个 Counter——维度缺失会导致聚合失效 - 路径要标准化:把
/user/123归一为/user/:id,否则指标爆炸;Gin 的c.FullPath()可直接获取路由模板 - 别在中间件里调
prometheus.MustRegister()——启动时注册一次即可,运行时只调.WithLabelValues(...).Inc()
避免中间件顺序错误导致统计失真
统计中间件必须放在所有可能提前终止请求的中间件之后,否则会漏掉 panic、认证拒绝、超时等场景。
- 错误顺序:
recovery → logger → stats→ panic 时stats根本没执行 - 正确顺序:
stats → recovery → auth → yourHandler,确保无论是否 panic 或提前 return,stats的 defer 都能捕获最终状态 - 如果用了
gin-contrib/cors或gin-contrib/gzip,它们内部可能调WriteHeader,所以stats必须最外层包裹
注意 context.Context 超时对统计值的影响
HTTP 超时(如 http.Server.ReadTimeout)不会触发你的中间件,而是由 net/http 底层直接关闭连接,此时你记录的耗时会远小于真实超时时间,且状态码为 0。
- 无法靠中间件捕获这种超时,必须配合服务端指标(如
go_http_inflight_requests)和网络层监控(如 TCP RST 包)交叉验证 - 应用层超时(如
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second))可被捕获,但需确保你在 handler 内显式检查ctx.Err()并写对应状态码(如 408),否则中间件看到的仍是 0 - 别依赖
time.Since(start)作为“总耗时”——它不含 TCP 握手、TLS 协商、客户端发包延迟,真实 P99 延迟要从接入层(如 Nginx、ALB)取
真正难的是把不同来源的耗时对齐:中间件统计的是 handler 执行时间,不是端到端延迟;Prometheus 的 http_request_duration_seconds 直方图需要明确分桶边界,而 Go 默认不自动打点,这些细节不抠清楚,统计结果看着热闹,查问题时全作废。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










