package monitoring import ( "net/http" "strconv" "time" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "payouts/internal/service/monitoring/config" ) const ( METRICS_NAMESPACE = "payouts" ) // Metrics represents the metrics service type Metrics interface { GetMiddleware() func(next http.Handler) http.Handler } // metrics represents the metrics service implementation type metrics struct { httpDuration prometheus.ObserverVec } // NewMetrics instantiates metrics service func NewMetrics(config config.Metrics) (Metrics, error) { var httpDuration prometheus.ObserverVec if config.Http.HistogramEnabled { httpDuration = CreateHttpHistogram(config.Http.Buckets) } else { httpDuration = CreateHttpSummary(config.Http.Buckets) } return &metrics{ httpDuration: httpDuration, }, nil } // GetMiddleware returns the middleware to be used in mux router func (m *metrics) GetMiddleware() func(next http.Handler) http.Handler { return GetMiddleware(m.httpDuration) } func CreateHttpSummary(buckets []float64) *prometheus.SummaryVec { return promauto.NewSummaryVec(prometheus.SummaryOpts{ Name: "http_server_requests_seconds", Help: "Duration of HTTP requests.", }, []string{"uri", "method", "code"}) } func CreateHttpHistogram(buckets []float64) *prometheus.HistogramVec { return promauto.NewHistogramVec(prometheus.HistogramOpts{ Name: "http_server_requests_seconds", Help: "Duration of HTTP requests.", Buckets: buckets, }, []string{"uri", "method", "code"}) } func GetMiddleware(histogramVec prometheus.ObserverVec) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { route := mux.CurrentRoute(r) path, _ := route.GetPathTemplate() wd := &writerDelegate{ ResponseWriter: w, statusCode: http.StatusOK, } start := time.Now() defer func() { duration := time.Since(start).Seconds() code := strconv.Itoa(wd.statusCode) histogramVec.WithLabelValues(path, r.Method, code).Observe(duration) }() next.ServeHTTP(wd, r) }) } } type writerDelegate struct { http.ResponseWriter statusCode int } // override the WriteHeader method func (w *writerDelegate) WriteHeader(statusCode int) { w.statusCode = statusCode w.ResponseWriter.WriteHeader(statusCode) }