tavern/metrics/middleware.go

97 lines
2.5 KiB
Go

package metrics
import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type MetricsMiddleware struct {
Requests *prometheus.CounterVec
TotalTime *prometheus.HistogramVec
RequestSize *prometheus.SummaryVec
ResponseSize *prometheus.SummaryVec
}
func NewMetricsMiddleware(namespace, subsystem string, metricFactory promauto.Factory) MetricsMiddleware {
return MetricsMiddleware{
Requests: metricFactory.NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "request_total",
Help: "Total number of HTTP Requests made.",
}, []string{"status", "endpoint", "method"},
),
TotalTime: metricFactory.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "request_duration",
Help: "HTTP request latencies in seconds.",
}, []string{"status", "endpoint", "method"},
),
RequestSize: metricFactory.NewSummaryVec(
prometheus.SummaryOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "request_size",
Help: "HTTP request sizes in bytes.",
}, []string{"status", "endpoint", "method"},
),
ResponseSize: metricFactory.NewSummaryVec(
prometheus.SummaryOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "response_size",
Help: "HTTP request sizes in bytes.",
}, []string{"status", "endpoint", "method"},
),
}
}
func (mm MetricsMiddleware) Handle(c *gin.Context) {
start := time.Now()
c.Next()
status := strconv.Itoa(c.Writer.Status())
endpoint := c.FullPath()
method := c.Request.Method
lvs := []string{status, endpoint, method}
mm.Requests.WithLabelValues(lvs...).Inc()
mm.TotalTime.WithLabelValues(lvs...).Observe(time.Since(start).Seconds())
mm.RequestSize.WithLabelValues(lvs...).Observe(CalcRequestSize(c.Request))
mm.ResponseSize.WithLabelValues(lvs...).Observe(float64(c.Writer.Size()))
}
// CalcRequestSize returns the size of request object.
func CalcRequestSize(r *http.Request) float64 {
size := 0
if r.URL != nil {
size = len(r.URL.String())
}
size += len(r.Method)
size += len(r.Proto)
for name, values := range r.Header {
size += len(name)
for _, value := range values {
size += len(value)
}
}
size += len(r.Host)
// r.Form and r.MultipartForm are assumed to be included in r.URL.
if r.ContentLength != -1 {
size += int(r.ContentLength)
}
return float64(size)
}