package httpapi import ( "net/http" "strconv" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" ) var ( httpRequests = promauto.NewCounterVec(prometheus.CounterOpts{ Namespace: "voice_platform", Subsystem: "http", Name: "requests_total", Help: "Total HTTP requests by service, method, route and status.", }, []string{"service", "method", "route", "status"}) httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ Namespace: "voice_platform", Subsystem: "http", Name: "request_duration_seconds", Help: "HTTP request duration by service, method and route.", Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5}, }, []string{"service", "method", "route"}) httpInflight = promauto.NewGaugeVec(prometheus.GaugeOpts{ Namespace: "voice_platform", Subsystem: "http", Name: "inflight_requests", Help: "Current in-flight HTTP requests by service.", }, []string{"service"}) ) func MetricsHandler() http.Handler { return promhttp.Handler() } func metricsMiddleware(serviceName string, next http.Handler) http.Handler { return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { startedAt := time.Now() httpInflight.WithLabelValues(serviceName).Inc() defer httpInflight.WithLabelValues(serviceName).Dec() recorder := &statusRecorder{ResponseWriter: writer, status: http.StatusOK} defer func() { route := request.Pattern if route == "" { route = "unmatched" } httpRequests.WithLabelValues(serviceName, request.Method, route, strconv.Itoa(recorder.status)).Inc() httpDuration.WithLabelValues(serviceName, request.Method, route).Observe(time.Since(startedAt).Seconds()) }() next.ServeHTTP(recorder, request) }) } type statusRecorder struct { http.ResponseWriter status int bytes int } func (r *statusRecorder) WriteHeader(status int) { r.status = status r.ResponseWriter.WriteHeader(status) } func (r *statusRecorder) Write(body []byte) (int, error) { written, err := r.ResponseWriter.Write(body) r.bytes += written return written, err } func (r *statusRecorder) Unwrap() http.ResponseWriter { return r.ResponseWriter }