From 3713b2a40b20b517d7aec04598d5714133202a05 Mon Sep 17 00:00:00 2001 From: Alejandro Visiedo Date: Mon, 23 Sep 2024 07:30:10 +0200 Subject: [PATCH] feat(HMS-4757): measure header and body request sizes This change add metrics to retrieve the size for header and body requests, by adding the necessary changes to metrics structure, and the behavior to store the metrics at the metrics middleware. https://issues.redhat.com/browse/HMS-4757 Signed-off-by: Alejandro Visiedo --- internal/infrastructure/middleware/metrics.go | 24 +++++++++++++++++++ .../infrastructure/middleware/metrics_test.go | 1 + internal/metrics/metrics.go | 20 +++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/internal/infrastructure/middleware/metrics.go b/internal/infrastructure/middleware/metrics.go index 60024615..14bbd1c9 100644 --- a/internal/infrastructure/middleware/metrics.go +++ b/internal/infrastructure/middleware/metrics.go @@ -2,11 +2,14 @@ package middleware import ( "errors" + "log/slog" "strconv" + "strings" "time" "github.com/labstack/echo/v4" echo_middleware "github.com/labstack/echo/v4/middleware" + "github.com/podengo-project/idmsvc-backend/internal/infrastructure/context" "github.com/podengo-project/idmsvc-backend/internal/metrics" "github.com/prometheus/client_golang/prometheus" ) @@ -40,6 +43,8 @@ func MetricsMiddlewareWithConfig(config *MetricsConfig) echo.MiddlewareFunc { err := next(ctx) + logger := context.LogFromCtx(ctx.Request().Context()) + method := ctx.Request().Method path := MatchedRoute(ctx) status := ctx.Response().Status @@ -50,6 +55,25 @@ func MetricsMiddlewareWithConfig(config *MetricsConfig) echo.MiddlewareFunc { status = httpErr.Code } statusStr := strconv.Itoa(status) + headerBuf := strings.Builder{} + headerSize := 0.0 + if errHeaderBuf := ctx.Request().Header.Write(&headerBuf); errHeaderBuf == nil { + headerSize = float64(len(headerBuf.String())) + config.Metrics.HTTPRequestHeaderSize.WithLabelValues(statusStr, method, path).Observe(headerSize) + } else { + logger.Warn("writing headers in string buffer", + slog.String("err", errHeaderBuf.Error()), + ) + } + bodySize := float64(ctx.Request().ContentLength) + config.Metrics.HTTPRequestBodySize.WithLabelValues(statusStr, method, path).Observe(bodySize) + logger.Debug("measured request size", + slog.String("status", statusStr), + slog.String("method", method), + slog.String("path", path), + slog.Float64("header_size", headerSize), + slog.Float64("body_size", bodySize), + ) config.Metrics.HTTPRequestDuration.WithLabelValues(statusStr, method, path).Observe(time.Since(start).Seconds()) diff --git a/internal/infrastructure/middleware/metrics_test.go b/internal/infrastructure/middleware/metrics_test.go index 4e808ce6..65858d78 100644 --- a/internal/infrastructure/middleware/metrics_test.go +++ b/internal/infrastructure/middleware/metrics_test.go @@ -69,6 +69,7 @@ func TestMetricsMiddlewareWithConfigCreation(t *testing.T) { } e := echo.New() + e.Use(ContextLogConfig(&LogConfig{})) m := MetricsMiddlewareWithConfig(config) e.Use(m) path := "/api/idmsvc/v1/domains" diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index e1ef3eaf..8b9c0166 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -13,8 +13,12 @@ const ( // Metrics holds all the Prometheus metrics for the application type Metrics struct { - // HTTPRequestDuration is a histogram that measures the duration of HTTP requests + // HTTPRequestDuration is a histogram that measures the duration of the HTTP requests HTTPRequestDuration *prometheus.HistogramVec + // HTTPRequestHeaderSize is a histogram that measures the size of the HTTP request headers. + HTTPRequestHeaderSize *prometheus.HistogramVec + // HTTPRequestBodySize is a histogram that measures the size of the HTTP request bodys. + HTTPRequestBodySize *prometheus.HistogramVec reg *prometheus.Registry } @@ -37,6 +41,20 @@ func NewMetrics(reg *prometheus.Registry) *Metrics { Help: "Duration of HTTP requests", Buckets: prometheus.ExponentialBuckets(0.0005, 2, 20), }, []string{"status", "method", "path"}), + HTTPRequestHeaderSize: promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{ + Namespace: NameSpace, + Name: "http_request_header_size", + Help: "Size of the HTTP request headers", + // Bucket limited to 32KB + Buckets: []float64{1024, 2 * 1024, 4 * 1024, 8 * 1024, 16 * 1024, 32 * 1024}, + }, []string{"status", "method", "path"}), + HTTPRequestBodySize: promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{ + Namespace: NameSpace, + Name: "http_request_body_size", + Help: "Size of the HTTP request bodies", + // Bucket limited to 128KB + Buckets: []float64{1024, 2 * 1024, 4 * 1024, 8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024, 128 * 1024}, + }, []string{"status", "method", "path"}), } reg.MustRegister(collectors.NewBuildInfoCollector())