Files
pocket-id/backend/internal/middleware/metrics.go

109 lines
2.4 KiB
Go

package middleware
import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
)
type PrometheusMetricsMiddleware struct {
reqCnt *prometheus.CounterVec
reqDur *prometheus.HistogramVec
reqSz, resSz prometheus.Summary
}
func computeApproximateRequestSize(r *http.Request) int {
s := 0
if r.URL != nil {
s = len(r.URL.Path)
}
s += len(r.Method)
s += len(r.Proto)
for name, values := range r.Header {
s += len(name)
for _, value := range values {
s += len(value)
}
}
s += len(r.Host)
if r.ContentLength != -1 {
s += int(r.ContentLength)
}
return s
}
func NewPrometheusMetricsMiddleware() *PrometheusMetricsMiddleware {
m := &PrometheusMetricsMiddleware{}
m.reqCnt = prometheus.NewCounterVec(
prometheus.CounterOpts{
Subsystem: "pocket_id",
Name: "requests_total",
Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
},
[]string{"code", "method", "handler", "host", "url"},
)
m.reqDur = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Subsystem: "pocket_id",
Name: "request_duration_seconds",
Help: "The HTTP request latencies in seconds.",
},
[]string{"code", "method", "url"},
)
m.resSz = prometheus.NewSummary(
prometheus.SummaryOpts{
Subsystem: "pocket_id",
Name: "response_size_bytes",
Help: "The HTTP response sizes in bytes.",
},
)
m.reqSz = prometheus.NewSummary(
prometheus.SummaryOpts{
Subsystem: "pocket_id",
Name: "request_size_bytes",
Help: "The HTTP request sizes in bytes.",
},
)
prometheus.Register(m.reqCnt)
prometheus.Register(m.reqDur)
prometheus.Register(m.resSz)
prometheus.Register(m.reqSz)
return m
}
func (m *PrometheusMetricsMiddleware) Add() gin.HandlerFunc {
return func(c *gin.Context) {
// Skip /metrics URL calls, as promhttp already tracks them
if c.Request.URL.Path == "/metrics" {
c.Next()
return
}
start := time.Now()
reqSz := computeApproximateRequestSize(c.Request)
c.Next()
status := strconv.Itoa(c.Writer.Status())
elapsed := float64(time.Since(start)) / float64(time.Second)
resSz := float64(c.Writer.Size())
m.reqDur.WithLabelValues(status, c.Request.Method, c.Request.URL.Path).Observe(elapsed)
m.reqCnt.WithLabelValues(status, c.Request.Method, c.HandlerName(), c.Request.Host, c.Request.URL.Path).Inc()
m.reqSz.Observe(float64(reqSz))
m.resSz.Observe(resSz)
}
}