package middleware import ( "net/http" "strconv" "time" "github.com/go-chi/chi/v5" chimw "github.com/go-chi/chi/v5/middleware" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) var ( httpRequestsTotal = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "govai_http_requests_total", Help: "HTTP 请求总数,按方法、路由模板、状态码统计。", }, []string{"method", "route", "status"}, ) httpRequestDuration = promauto.NewHistogramVec( prometheus.HistogramOpts{ Name: "govai_http_request_duration_seconds", Help: "HTTP 请求耗时(秒),按方法与路由模板统计。", Buckets: prometheus.DefBuckets, }, []string{"method", "route"}, ) ) // Metrics 是记录 Prometheus 指标的全局中间件。 // 使用 chi 的路由模板(而非原始路径)作为标签,避免高基数。 func Metrics(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() ww := chimw.NewWrapResponseWriter(w, r.ProtoMajor) next.ServeHTTP(ww, r) route := chi.RouteContext(r.Context()).RoutePattern() if route == "" { route = "unmatched" } status := ww.Status() if status == 0 { status = http.StatusOK } httpRequestsTotal.WithLabelValues(r.Method, route, strconv.Itoa(status)).Inc() httpRequestDuration.WithLabelValues(r.Method, route).Observe(time.Since(start).Seconds()) }) }