From 2d7c6605da06ce66d4ede086c7d2d9851781d117 Mon Sep 17 00:00:00 2001 From: ensiouel <ensiouel@gmail.com> Date: Sun, 17 Dec 2023 08:43:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BC=D0=B5=D1=82=D1=80=D0=B8=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE?= =?UTF-8?q?=D0=B2=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/templates/middleware/metrics | 39 +++ go.mod | 1 + go.sum | 2 + pkg/items/middleware/metrics_middleware.go | 266 +++++++++++++++++++++ pkg/metrics/request.go | 40 ++++ 5 files changed, 348 insertions(+) create mode 100644 assets/templates/middleware/metrics create mode 100644 pkg/items/middleware/metrics_middleware.go create mode 100644 pkg/metrics/request.go diff --git a/assets/templates/middleware/metrics b/assets/templates/middleware/metrics new file mode 100644 index 00000000..f55ffd51 --- /dev/null +++ b/assets/templates/middleware/metrics @@ -0,0 +1,39 @@ +import ( + "context" + + "github.com/prometheus/client_golang/prometheus" +) + +{{ $decorator := (or .Vars.DecoratorName "metricsMiddleware") }} +{{ $funcName := (or .Vars.FuncName ("MetricsMiddleware")) }} + +// {{ $decorator }} implements {{ .Interface.Type }} that is instrumented with metrics +type {{ $decorator }} struct { + requestMetrics *metrics.RequestMetrics + next {{ .Interface.Type }} +} + +// {{ $funcName }} instruments an implementation of the {{ $decorator }} with metrics +func {{ $funcName }} (requestMetrics *metrics.RequestMetrics) Middleware { + return func(next {{ .Interface.Type }}) {{ .Interface.Type }} { + return &{{ $decorator }}{ + requestMetrics: requestMetrics, + next: next, + } + } +} + +{{ range $method := .Interface.Methods }} + // {{ $method.Name }} implements {{ $.Interface.Type }} + func (m {{ $decorator }}) {{ $method.Declaration }} { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("{{ $.Interface.Name }}", "{{ $method.Name }}")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("{{ $.Interface.Name }}", "{{ $method.Name }}").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("{{ $.Interface.Name }}", "{{ $method.Name }}").Inc() + } + }() + {{ $method.Pass "m.next." }} + } +{{ end }} \ No newline at end of file diff --git a/go.mod b/go.mod index 86d2b2e6..c99f9da9 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/nats-io/nkeys v0.4.6 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect github.com/stretchr/objx v0.5.1 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect diff --git a/go.sum b/go.sum index d11a9be6..c7cd2871 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= diff --git a/pkg/items/middleware/metrics_middleware.go b/pkg/items/middleware/metrics_middleware.go new file mode 100644 index 00000000..489f39a6 --- /dev/null +++ b/pkg/items/middleware/metrics_middleware.go @@ -0,0 +1,266 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../assets/templates/middleware/metrics +// gowrap: http://github.com/hexdigest/gowrap + +package middleware + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/items -i Items -t ../../assets/templates/middleware/metrics -o metrics_middleware.go -l "" + +import ( + "context" + + "git.perx.ru/perxis/perxis-go/pkg/items" + "git.perx.ru/perxis/perxis-go/pkg/metrics" + "git.perx.ru/perxis/perxis-go/pkg/schema" + "github.com/prometheus/client_golang/prometheus" +) + +// metricsMiddleware implements items.Items that is instrumented with metrics +type metricsMiddleware struct { + requestMetrics *metrics.RequestMetrics + next items.Items +} + +// MetricsMiddleware instruments an implementation of the items.Items with metrics +func MetricsMiddleware(requestMetrics *metrics.RequestMetrics) Middleware { + return func(next items.Items) items.Items { + return &metricsMiddleware{ + requestMetrics: requestMetrics, + next: next, + } + } +} + +// Aggregate implements Items +func (m metricsMiddleware) Aggregate(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregateOptions) (result map[string]interface{}, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Aggregate")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Aggregate").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Aggregate").Inc() + } + }() + return m.next.Aggregate(ctx, spaceId, envId, collectionId, filter, options...) +} + +// AggregatePublished implements Items +func (m metricsMiddleware) AggregatePublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregatePublishedOptions) (result map[string]interface{}, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "AggregatePublished")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "AggregatePublished").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "AggregatePublished").Inc() + } + }() + return m.next.AggregatePublished(ctx, spaceId, envId, collectionId, filter, options...) +} + +// Archive implements Items +func (m metricsMiddleware) Archive(ctx context.Context, item *items.Item, options ...*items.ArchiveOptions) (err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Archive")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Archive").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Archive").Inc() + } + }() + return m.next.Archive(ctx, item, options...) +} + +// Create implements Items +func (m metricsMiddleware) Create(ctx context.Context, item *items.Item, opts ...*items.CreateOptions) (created *items.Item, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Create")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Create").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Create").Inc() + } + }() + return m.next.Create(ctx, item, opts...) +} + +// Delete implements Items +func (m metricsMiddleware) Delete(ctx context.Context, item *items.Item, options ...*items.DeleteOptions) (err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Delete")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Delete").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Delete").Inc() + } + }() + return m.next.Delete(ctx, item, options...) +} + +// Find implements Items +func (m metricsMiddleware) Find(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindOptions) (items []*items.Item, total int, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Find")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Find").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Find").Inc() + } + }() + return m.next.Find(ctx, spaceId, envId, collectionId, filter, options...) +} + +// FindArchived implements Items +func (m metricsMiddleware) FindArchived(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindArchivedOptions) (items []*items.Item, total int, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "FindArchived")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "FindArchived").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "FindArchived").Inc() + } + }() + return m.next.FindArchived(ctx, spaceId, envId, collectionId, filter, options...) +} + +// FindPublished implements Items +func (m metricsMiddleware) FindPublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindPublishedOptions) (items []*items.Item, total int, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "FindPublished")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "FindPublished").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "FindPublished").Inc() + } + }() + return m.next.FindPublished(ctx, spaceId, envId, collectionId, filter, options...) +} + +// Get implements Items +func (m metricsMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetOptions) (item *items.Item, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Get")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Get").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Get").Inc() + } + }() + return m.next.Get(ctx, spaceId, envId, collectionId, itemId, options...) +} + +// GetPublished implements Items +func (m metricsMiddleware) GetPublished(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetPublishedOptions) (item *items.Item, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "GetPublished")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "GetPublished").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "GetPublished").Inc() + } + }() + return m.next.GetPublished(ctx, spaceId, envId, collectionId, itemId, options...) +} + +// GetRevision implements Items +func (m metricsMiddleware) GetRevision(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, revisionId string, options ...*items.GetRevisionOptions) (item *items.Item, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "GetRevision")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "GetRevision").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "GetRevision").Inc() + } + }() + return m.next.GetRevision(ctx, spaceId, envId, collectionId, itemId, revisionId, options...) +} + +// Introspect implements Items +func (m metricsMiddleware) Introspect(ctx context.Context, item *items.Item, opts ...*items.IntrospectOptions) (itm *items.Item, sch *schema.Schema, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Introspect")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Introspect").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Introspect").Inc() + } + }() + return m.next.Introspect(ctx, item, opts...) +} + +// ListRevisions implements Items +func (m metricsMiddleware) ListRevisions(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.ListRevisionsOptions) (items []*items.Item, err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "ListRevisions")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "ListRevisions").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "ListRevisions").Inc() + } + }() + return m.next.ListRevisions(ctx, spaceId, envId, collectionId, itemId, options...) +} + +// Publish implements Items +func (m metricsMiddleware) Publish(ctx context.Context, item *items.Item, options ...*items.PublishOptions) (err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Publish")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Publish").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Publish").Inc() + } + }() + return m.next.Publish(ctx, item, options...) +} + +// Unarchive implements Items +func (m metricsMiddleware) Unarchive(ctx context.Context, item *items.Item, options ...*items.UnarchiveOptions) (err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Unarchive")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Unarchive").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Unarchive").Inc() + } + }() + return m.next.Unarchive(ctx, item, options...) +} + +// Undelete implements Items +func (m metricsMiddleware) Undelete(ctx context.Context, item *items.Item, options ...*items.UndeleteOptions) (err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Undelete")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Undelete").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Undelete").Inc() + } + }() + return m.next.Undelete(ctx, item, options...) +} + +// Unpublish implements Items +func (m metricsMiddleware) Unpublish(ctx context.Context, item *items.Item, options ...*items.UnpublishOptions) (err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Unpublish")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Unpublish").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Unpublish").Inc() + } + }() + return m.next.Unpublish(ctx, item, options...) +} + +// Update implements Items +func (m metricsMiddleware) Update(ctx context.Context, item *items.Item, options ...*items.UpdateOptions) (err error) { + timer := prometheus.NewTimer(m.requestMetrics.DurationSeconds.WithLabelValues("Items", "Update")) + defer func() { + timer.ObserveDuration() + m.requestMetrics.Total.WithLabelValues("Items", "Update").Inc() + if err != nil { + m.requestMetrics.FailedTotal.WithLabelValues("Items", "Update").Inc() + } + }() + return m.next.Update(ctx, item, options...) +} diff --git a/pkg/metrics/request.go b/pkg/metrics/request.go new file mode 100644 index 00000000..4b7ac85c --- /dev/null +++ b/pkg/metrics/request.go @@ -0,0 +1,40 @@ +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +type RequestMetrics struct { + Total *prometheus.CounterVec + FailedTotal *prometheus.CounterVec + DurationSeconds *prometheus.HistogramVec +} + +// NewRequestMetrics +// +// # Example metrics +// +// svc_request_duration_seconds_bucket{service="Collections",method="Get",le="+Inf"} 2 +// +// svc_request_duration_seconds_sum{service="Collections",method="Get"} 0.711158607 +// +// svc_request_duration_seconds_count{service="Collections",method="Get"} 2 +// +// svc_requests_failed_total{service="Collections",method="Get",error="not found"} 1 +// +// svc_requests_total{service="Collections",method="Get"} 2 +func NewRequestMetrics(registry prometheus.Registerer, buckets []float64) *RequestMetrics { + return &RequestMetrics{ + Total: promauto.With(registry).NewCounterVec(prometheus.CounterOpts{ + Name: "svc_requests_total", + }, []string{"service", "method"}), + FailedTotal: promauto.With(registry).NewCounterVec(prometheus.CounterOpts{ + Name: "svc_requests_failed_total", + }, []string{"service", "method", "error"}), + DurationSeconds: promauto.With(registry).NewHistogramVec(prometheus.HistogramOpts{ + Name: "svc_request_duration_seconds", + Buckets: buckets, + }, []string{"service", "method"}), + } +} -- GitLab