From 9e06b12aa06244673b111e2cd46c8f375cb268a2 Mon Sep 17 00:00:00 2001 From: ming luo Date: Tue, 20 Jul 2021 13:43:04 -0400 Subject: [PATCH 1/2] custom labels --- metrics/metrics.go | 2 ++ metrics/prometheus/prometheus.go | 33 +++++++++++++++++++++++++++---- middleware/echo/echo.go | 2 ++ middleware/fasthttp/fasthttp.go | 2 ++ middleware/gin/gin.go | 2 ++ middleware/gorestful/gorestful.go | 2 ++ middleware/middleware.go | 1 + middleware/std/std.go | 2 ++ 8 files changed, 42 insertions(+), 4 deletions(-) diff --git a/metrics/metrics.go b/metrics/metrics.go index f4fa46a..dff3fb8 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -16,6 +16,8 @@ type HTTPReqProperties struct { Method string // Code is the response of the request. Code string + // CustomizedLabels is the customized header + Labels map[string]string } // HTTPProperties are the metric properties for the global server metrics. diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 41e95b5..721b116 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -2,6 +2,7 @@ package prometheus import ( "context" + "sort" "time" "github.com/prometheus/client_golang/prometheus" @@ -30,6 +31,8 @@ type Config struct { MethodLabel string // ServiceLabel is the name that will be set to the service label, by default is `service`. ServiceLabel string + // CustomizedLabels is the labels from the header + CustomizedLabels []string } func (c *Config) defaults() { @@ -73,6 +76,13 @@ type recorder struct { func NewRecorder(cfg Config) metrics.Recorder { cfg.defaults() + customLabels := []string{} + if cfg.CustomizedLabels != nil && len(cfg.CustomizedLabels) > 0 { + customLabels = cfg.CustomizedLabels + sort.Strings(customLabels) + } + + labels := append([]string{cfg.ServiceLabel, cfg.HandlerIDLabel, cfg.MethodLabel, cfg.StatusCodeLabel}, customLabels...) r := &recorder{ httpRequestDurHistogram: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: cfg.Prefix, @@ -80,7 +90,7 @@ func NewRecorder(cfg Config) metrics.Recorder { Name: "request_duration_seconds", Help: "The latency of the HTTP requests.", Buckets: cfg.DurationBuckets, - }, []string{cfg.ServiceLabel, cfg.HandlerIDLabel, cfg.MethodLabel, cfg.StatusCodeLabel}), + }, labels), httpResponseSizeHistogram: prometheus.NewHistogramVec(prometheus.HistogramOpts{ Namespace: cfg.Prefix, @@ -88,7 +98,7 @@ func NewRecorder(cfg Config) metrics.Recorder { Name: "response_size_bytes", Help: "The size of the HTTP responses.", Buckets: cfg.SizeBuckets, - }, []string{cfg.ServiceLabel, cfg.HandlerIDLabel, cfg.MethodLabel, cfg.StatusCodeLabel}), + }, labels), httpRequestsInflight: prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: cfg.Prefix, @@ -107,12 +117,27 @@ func NewRecorder(cfg Config) metrics.Recorder { return r } +func (r recorder) getLabelValues(p metrics.HTTPReqProperties) []string { + keys := []string{} + for key, _ := range p.Labels { + keys = append(keys, key) + } + sort.Strings(keys) + labels := []string{p.Service, p.ID, p.Method, p.Code} + for _, key := range keys { + labels = append(labels, p.Labels[key]) + } + return labels +} + func (r recorder) ObserveHTTPRequestDuration(_ context.Context, p metrics.HTTPReqProperties, duration time.Duration) { - r.httpRequestDurHistogram.WithLabelValues(p.Service, p.ID, p.Method, p.Code).Observe(duration.Seconds()) + labels := r.getLabelValues(p) + r.httpRequestDurHistogram.WithLabelValues(labels...).Observe(duration.Seconds()) } func (r recorder) ObserveHTTPResponseSize(_ context.Context, p metrics.HTTPReqProperties, sizeBytes int64) { - r.httpResponseSizeHistogram.WithLabelValues(p.Service, p.ID, p.Method, p.Code).Observe(float64(sizeBytes)) + labels := r.getLabelValues(p) + r.httpResponseSizeHistogram.WithLabelValues(labels...).Observe(float64(sizeBytes)) } func (r recorder) AddInflightRequests(_ context.Context, p metrics.HTTPProperties, quantity int) { diff --git a/middleware/echo/echo.go b/middleware/echo/echo.go index 62d75bd..0b8c741 100644 --- a/middleware/echo/echo.go +++ b/middleware/echo/echo.go @@ -35,3 +35,5 @@ func (r *reporter) URLPath() string { return r.c.Request().URL.Path } func (r *reporter) StatusCode() int { return r.c.Response().Status } func (r *reporter) BytesWritten() int64 { return r.c.Response().Size } + +func (r *reporter) CustomHeaders() map[string]string { return make(map[string]string) } diff --git a/middleware/fasthttp/fasthttp.go b/middleware/fasthttp/fasthttp.go index a7ce124..73df89f 100644 --- a/middleware/fasthttp/fasthttp.go +++ b/middleware/fasthttp/fasthttp.go @@ -40,3 +40,5 @@ func (r reporter) StatusCode() int { func (r reporter) BytesWritten() int64 { return int64(len(r.c.Response.Body())) } + +func (r reporter) CustomHeaders() map[string]string { return make(map[string]string) } diff --git a/middleware/gin/gin.go b/middleware/gin/gin.go index 06ca7a1..0bfdb7b 100644 --- a/middleware/gin/gin.go +++ b/middleware/gin/gin.go @@ -32,3 +32,5 @@ func (r *reporter) URLPath() string { return r.c.Request.URL.Path } func (r *reporter) StatusCode() int { return r.c.Writer.Status() } func (r *reporter) BytesWritten() int64 { return int64(r.c.Writer.Size()) } + +func (r *reporter) CustomHeaders() map[string]string { return make(map[string]string) } diff --git a/middleware/gorestful/gorestful.go b/middleware/gorestful/gorestful.go index 4a2fec2..0e5bf86 100644 --- a/middleware/gorestful/gorestful.go +++ b/middleware/gorestful/gorestful.go @@ -33,3 +33,5 @@ func (r *reporter) URLPath() string { return r.req.Request.URL.Path } func (r *reporter) StatusCode() int { return r.resp.StatusCode() } func (r *reporter) BytesWritten() int64 { return int64(r.resp.ContentLength()) } + +func (r *reporter) CustomHeaders() map[string]string { return make(map[string]string) } diff --git a/middleware/middleware.go b/middleware/middleware.go index 079e77c..14a216f 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -124,5 +124,6 @@ type Reporter interface { Context() context.Context URLPath() string StatusCode() int + CustomHeaders() map[string]string BytesWritten() int64 } diff --git a/middleware/std/std.go b/middleware/std/std.go index 5532f5e..b5b7cb1 100644 --- a/middleware/std/std.go +++ b/middleware/std/std.go @@ -52,6 +52,8 @@ func (s *stdReporter) StatusCode() int { return s.w.statusCode } func (s *stdReporter) BytesWritten() int64 { return int64(s.w.bytesWritten) } +func (s *stdReporter) CustomHeaders() map[string]string { return make(map[string]string) } + // responseWriterInterceptor is a simple wrapper to intercept set data on a // ResponseWriter. type responseWriterInterceptor struct { From 939292b273a5993afd35a8e4ff0223286f637e61 Mon Sep 17 00:00:00 2001 From: ming luo Date: Tue, 20 Jul 2021 16:44:12 -0400 Subject: [PATCH 2/2] add head labels in the middleware --- middleware/middleware.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/middleware/middleware.go b/middleware/middleware.go index 14a216f..f9cbb4b 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -99,11 +99,16 @@ func (m Middleware) Measure(handlerID string, reporter Reporter, next func()) { code = strconv.Itoa(reporter.StatusCode()) } + labels := make(map[string]string) + for k, v := range reporter.CustomHeaders() { + labels[k] = v + } props := metrics.HTTPReqProperties{ Service: m.cfg.Service, ID: hid, Method: reporter.Method(), Code: code, + Labels: labels, } m.cfg.Recorder.ObserveHTTPRequestDuration(ctx, props, duration)