diff --git a/assets/templates/middleware/telemetry b/assets/templates/middleware/telemetry index 3e893c09f161d2e832544f8bca625b6d63b3d990..0640645f8f9563a0ed8665c2c8a507f8e850b075 100644 --- a/assets/templates/middleware/telemetry +++ b/assets/templates/middleware/telemetry @@ -1,8 +1,14 @@ import ( "context" + "strings" + "time" + "git.perx.ru/perxis/perxis-go/pkg/items" + "git.perx.ru/perxis/perxis-go/pkg/metrics" + "git.perx.ru/perxis/perxis-go/pkg/schema" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -13,14 +19,18 @@ import ( type {{$decorator}} struct { {{.Interface.Type}} _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // {{$funcName}} returns {{$decorator}} -func {{$funcName}} (base {{.Interface.Type}}, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) {{$decorator}} { +func {{$funcName}} (base {{.Interface.Type}}, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) {{$decorator}} { d := {{$decorator}} { {{.Interface.Name}}: base, _instance: instance, + serviceName: strings.ToLower("{{ $.Interface.Name }}"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -34,11 +44,24 @@ func {{$funcName}} (base {{.Interface.Type}}, instance string, spanDecorator ... {{if $method.AcceptsContext}} // {{$method.Name}} implements {{$.Interface.Type}} func (_d {{$decorator}}) {{$method.Declaration}} { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("{{ $method.Name }}"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "{{$.Interface.Name}}.{{$method.Name}}") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, {{$method.ParamsMap}}, {{$method.ResultsMap}}) }{{- if $method.ReturnsError}} else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/images/middleware/telemetry_middleware.go b/images/middleware/telemetry_middleware.go index 2c0aac8eeaa3507d16ee852fd47b4a46569bc827..607d7e94bbd410d1d04bf5a25a5cb78f56b92af3 100644 --- a/images/middleware/telemetry_middleware.go +++ b/images/middleware/telemetry_middleware.go @@ -8,11 +8,15 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/images" "git.perx.ru/perxis/perxis-go/pkg/files" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -20,14 +24,18 @@ import ( type telemetryMiddleware struct { images.Images _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base images.Images, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base images.Images, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Images: base, - _instance: instance, + Images: base, + _instance: instance, + serviceName: strings.ToLower("Images"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -39,8 +47,19 @@ func TelemetryMiddleware(base images.Images, instance string, spanDecorator ...f // Get implements images.Images func (_d telemetryMiddleware) Get(ctx context.Context, source *files.File, opts *images.GetOptions) (result *files.File, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Images.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -49,6 +68,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, source *files.File, opts "result": result, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/clients/middleware/telemetry_middleware.go b/pkg/clients/middleware/telemetry_middleware.go index d8536b10fcac0a1f237c688fe9a8bde3db93fdcf..29d91606c9b847155cdfe100861903c3d3a6ae23 100644 --- a/pkg/clients/middleware/telemetry_middleware.go +++ b/pkg/clients/middleware/telemetry_middleware.go @@ -8,10 +8,14 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/clients" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { clients.Clients _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base clients.Clients, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base clients.Clients, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Clients: base, - _instance: instance, + Clients: base, + _instance: instance, + serviceName: strings.ToLower("Clients"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,8 +46,19 @@ func TelemetryMiddleware(base clients.Clients, instance string, spanDecorator .. // Create implements clients.Clients func (_d telemetryMiddleware) Create(ctx context.Context, client *clients.Client) (created *clients.Client, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Clients.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -47,6 +66,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, client *clients.Client "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -59,8 +80,19 @@ func (_d telemetryMiddleware) Create(ctx context.Context, client *clients.Client // Delete implements clients.Clients func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, id string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Clients.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -68,6 +100,8 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, id str "id": id}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -80,8 +114,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, id str // Enable implements clients.Clients func (_d telemetryMiddleware) Enable(ctx context.Context, spaceId string, id string, enable bool) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Enable"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Clients.Enable") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -90,6 +135,8 @@ func (_d telemetryMiddleware) Enable(ctx context.Context, spaceId string, id str "enable": enable}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -102,8 +149,19 @@ func (_d telemetryMiddleware) Enable(ctx context.Context, spaceId string, id str // Get implements clients.Clients func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, id string) (client *clients.Client, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Clients.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -112,6 +170,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, id string "client": client, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -124,8 +184,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, id string // GetBy implements clients.Clients func (_d telemetryMiddleware) GetBy(ctx context.Context, spaceId string, params *clients.GetByParams) (client *clients.Client, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetBy"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Clients.GetBy") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -134,6 +205,8 @@ func (_d telemetryMiddleware) GetBy(ctx context.Context, spaceId string, params "client": client, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -146,8 +219,19 @@ func (_d telemetryMiddleware) GetBy(ctx context.Context, spaceId string, params // List implements clients.Clients func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (clients []*clients.Client, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("List"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Clients.List") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -155,6 +239,8 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (clients "clients": clients, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -167,14 +253,27 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (clients // Update implements clients.Clients func (_d telemetryMiddleware) Update(ctx context.Context, client *clients.Client) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Clients.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "client": client}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/collaborators/middleware/telemetry_middleware.go b/pkg/collaborators/middleware/telemetry_middleware.go index 6a824f1e0a31c585d07b7ebb1bbf391963b0ca63..1989419bf402a2f583035d69f8fef4e003547a03 100644 --- a/pkg/collaborators/middleware/telemetry_middleware.go +++ b/pkg/collaborators/middleware/telemetry_middleware.go @@ -8,10 +8,14 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/collaborators" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { collaborators.Collaborators _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base collaborators.Collaborators, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base collaborators.Collaborators, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Collaborators: base, - _instance: instance, + Collaborators: base, + _instance: instance, + serviceName: strings.ToLower("Collaborators"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,8 +46,19 @@ func TelemetryMiddleware(base collaborators.Collaborators, instance string, span // Get implements collaborators.Collaborators func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, subject string) (role string, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collaborators.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -48,6 +67,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, subject s "role": role, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -60,8 +81,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, subject s // ListCollaborators implements collaborators.Collaborators func (_d telemetryMiddleware) ListCollaborators(ctx context.Context, spaceId string) (collaborators []*collaborators.Collaborator, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListCollaborators"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collaborators.ListCollaborators") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -69,6 +101,8 @@ func (_d telemetryMiddleware) ListCollaborators(ctx context.Context, spaceId str "collaborators": collaborators, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -81,8 +115,19 @@ func (_d telemetryMiddleware) ListCollaborators(ctx context.Context, spaceId str // ListSpaces implements collaborators.Collaborators func (_d telemetryMiddleware) ListSpaces(ctx context.Context, subject string) (spaces []*collaborators.Collaborator, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListSpaces"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collaborators.ListSpaces") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -90,6 +135,8 @@ func (_d telemetryMiddleware) ListSpaces(ctx context.Context, subject string) (s "spaces": spaces, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -102,8 +149,19 @@ func (_d telemetryMiddleware) ListSpaces(ctx context.Context, subject string) (s // Remove implements collaborators.Collaborators func (_d telemetryMiddleware) Remove(ctx context.Context, spaceId string, subject string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Remove"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collaborators.Remove") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -111,6 +169,8 @@ func (_d telemetryMiddleware) Remove(ctx context.Context, spaceId string, subjec "subject": subject}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -123,8 +183,19 @@ func (_d telemetryMiddleware) Remove(ctx context.Context, spaceId string, subjec // Set implements collaborators.Collaborators func (_d telemetryMiddleware) Set(ctx context.Context, spaceId string, subject string, role string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Set"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collaborators.Set") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -133,6 +204,8 @@ func (_d telemetryMiddleware) Set(ctx context.Context, spaceId string, subject s "role": role}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/collections/middleware/telemetry_middleware.go b/pkg/collections/middleware/telemetry_middleware.go index 74a0d685c37c6b6d8bdb0b6453f50cfea83e7f07..51d2fc769e40d7a01128d8b2443fe6373154e028 100644 --- a/pkg/collections/middleware/telemetry_middleware.go +++ b/pkg/collections/middleware/telemetry_middleware.go @@ -8,11 +8,15 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/collections" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/schema" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -20,14 +24,18 @@ import ( type telemetryMiddleware struct { collections.Collections _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base collections.Collections, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base collections.Collections, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Collections: base, - _instance: instance, + Collections: base, + _instance: instance, + serviceName: strings.ToLower("Collections"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -39,8 +47,19 @@ func TelemetryMiddleware(base collections.Collections, instance string, spanDeco // Create implements collections.Collections func (_d telemetryMiddleware) Create(ctx context.Context, collection *collections.Collection) (created *collections.Collection, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collections.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -48,6 +67,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, collection *collection "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -60,8 +81,19 @@ func (_d telemetryMiddleware) Create(ctx context.Context, collection *collection // Delete implements collections.Collections func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, envId string, collectionId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collections.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -70,6 +102,8 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, envId "collectionId": collectionId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -82,8 +116,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, envId // Get implements collections.Collections func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, options ...*collections.GetOptions) (collection *collections.Collection, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collections.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -94,6 +139,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str "collection": collection, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -106,8 +153,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str // List implements collections.Collections func (_d telemetryMiddleware) List(ctx context.Context, spaceId string, envId string, filter *collections.Filter) (collections []*collections.Collection, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("List"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collections.List") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -117,6 +175,8 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string, envId st "collections": collections, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -129,8 +189,19 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string, envId st // SetSchema implements collections.Collections func (_d telemetryMiddleware) SetSchema(ctx context.Context, spaceId string, envId string, collectionId string, schema *schema.Schema) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("SetSchema"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collections.SetSchema") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -140,6 +211,8 @@ func (_d telemetryMiddleware) SetSchema(ctx context.Context, spaceId string, env "schema": schema}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -152,8 +225,19 @@ func (_d telemetryMiddleware) SetSchema(ctx context.Context, spaceId string, env // SetState implements collections.Collections func (_d telemetryMiddleware) SetState(ctx context.Context, spaceId string, envId string, collectionId string, state *collections.StateInfo) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("SetState"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collections.SetState") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -163,6 +247,8 @@ func (_d telemetryMiddleware) SetState(ctx context.Context, spaceId string, envI "state": state}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -175,14 +261,27 @@ func (_d telemetryMiddleware) SetState(ctx context.Context, spaceId string, envI // Update implements collections.Collections func (_d telemetryMiddleware) Update(ctx context.Context, coll *collections.Collection) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Collections.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "coll": coll}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/delivery/middleware/telemetry_middleware.go b/pkg/delivery/middleware/telemetry_middleware.go index 9468782004efd635902eea18b2a174e411a49be4..1f5a62c50646700d5aad4f92f7ba9fb47d67ddf6 100644 --- a/pkg/delivery/middleware/telemetry_middleware.go +++ b/pkg/delivery/middleware/telemetry_middleware.go @@ -8,14 +8,18 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/collections" "git.perx.ru/perxis/perxis-go/pkg/delivery" "git.perx.ru/perxis/perxis-go/pkg/environments" "git.perx.ru/perxis/perxis-go/pkg/items" "git.perx.ru/perxis/perxis-go/pkg/locales" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -23,14 +27,18 @@ import ( type telemetryMiddleware struct { delivery.Delivery _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base delivery.Delivery, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base delivery.Delivery, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Delivery: base, - _instance: instance, + Delivery: base, + _instance: instance, + serviceName: strings.ToLower("Delivery"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -42,8 +50,19 @@ func TelemetryMiddleware(base delivery.Delivery, instance string, spanDecorator // Aggregate implements delivery.Delivery func (_d telemetryMiddleware) Aggregate(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregatePublishedOptions) (result map[string]interface{}, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Aggregate"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.Aggregate") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -55,6 +74,8 @@ func (_d telemetryMiddleware) Aggregate(ctx context.Context, spaceId string, env "result": result, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -67,8 +88,19 @@ func (_d telemetryMiddleware) Aggregate(ctx context.Context, spaceId string, env // FindItems implements delivery.Delivery func (_d telemetryMiddleware) FindItems(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindPublishedOptions) (items []*items.Item, total int, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("FindItems"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.FindItems") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -81,6 +113,8 @@ func (_d telemetryMiddleware) FindItems(ctx context.Context, spaceId string, env "total": total, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -93,8 +127,19 @@ func (_d telemetryMiddleware) FindItems(ctx context.Context, spaceId string, env // GetCollection implements delivery.Delivery func (_d telemetryMiddleware) GetCollection(ctx context.Context, spaceId string, envId string, collectionId string) (collection *collections.Collection, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetCollection"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.GetCollection") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -104,6 +149,8 @@ func (_d telemetryMiddleware) GetCollection(ctx context.Context, spaceId string, "collection": collection, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -116,8 +163,19 @@ func (_d telemetryMiddleware) GetCollection(ctx context.Context, spaceId string, // GetEnvironment implements delivery.Delivery func (_d telemetryMiddleware) GetEnvironment(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetEnvironment"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.GetEnvironment") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -126,6 +184,8 @@ func (_d telemetryMiddleware) GetEnvironment(ctx context.Context, spaceId string "env": env, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -138,8 +198,19 @@ func (_d telemetryMiddleware) GetEnvironment(ctx context.Context, spaceId string // GetItem implements delivery.Delivery func (_d telemetryMiddleware) GetItem(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetPublishedOptions) (item *items.Item, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetItem"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.GetItem") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -151,6 +222,8 @@ func (_d telemetryMiddleware) GetItem(ctx context.Context, spaceId string, envId "item": item, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -163,8 +236,19 @@ func (_d telemetryMiddleware) GetItem(ctx context.Context, spaceId string, envId // ListCollections implements delivery.Delivery func (_d telemetryMiddleware) ListCollections(ctx context.Context, spaceId string, envId string) (collections []*collections.Collection, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListCollections"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.ListCollections") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -173,6 +257,8 @@ func (_d telemetryMiddleware) ListCollections(ctx context.Context, spaceId strin "collections": collections, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -185,8 +271,19 @@ func (_d telemetryMiddleware) ListCollections(ctx context.Context, spaceId strin // ListEnvironments implements delivery.Delivery func (_d telemetryMiddleware) ListEnvironments(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListEnvironments"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.ListEnvironments") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -194,6 +291,8 @@ func (_d telemetryMiddleware) ListEnvironments(ctx context.Context, spaceId stri "envs": envs, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -206,8 +305,19 @@ func (_d telemetryMiddleware) ListEnvironments(ctx context.Context, spaceId stri // ListLocales implements delivery.Delivery func (_d telemetryMiddleware) ListLocales(ctx context.Context, spaceId string) (locales []*locales.Locale, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListLocales"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Delivery.ListLocales") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -215,6 +325,8 @@ func (_d telemetryMiddleware) ListLocales(ctx context.Context, spaceId string) ( "locales": locales, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/environments/middleware/telemetry_middleware.go b/pkg/environments/middleware/telemetry_middleware.go index 3e27536fb5458dc05dd2f81bbd3ff85830f09c03..b156a18846d98446a1a1dbf9eed23bfdea9f09bb 100644 --- a/pkg/environments/middleware/telemetry_middleware.go +++ b/pkg/environments/middleware/telemetry_middleware.go @@ -8,10 +8,14 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/environments" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { environments.Environments _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base environments.Environments, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base environments.Environments, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Environments: base, - _instance: instance, + Environments: base, + _instance: instance, + serviceName: strings.ToLower("Environments"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,8 +46,19 @@ func TelemetryMiddleware(base environments.Environments, instance string, spanDe // Create implements environments.Environments func (_d telemetryMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -47,6 +66,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, env *environments.Envi "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -59,8 +80,19 @@ func (_d telemetryMiddleware) Create(ctx context.Context, env *environments.Envi // Delete implements environments.Environments func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -68,6 +100,8 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, envId "envId": envId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -80,8 +114,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, envId // Get implements environments.Environments func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -90,6 +135,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str "env": env, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -102,8 +149,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str // List implements environments.Environments func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("List"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.List") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -111,6 +169,8 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (envs [] "envs": envs, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -123,8 +183,19 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (envs [] // Migrate implements environments.Environments func (_d telemetryMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Migrate"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.Migrate") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -133,6 +204,8 @@ func (_d telemetryMiddleware) Migrate(ctx context.Context, spaceId string, envId "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -145,8 +218,19 @@ func (_d telemetryMiddleware) Migrate(ctx context.Context, spaceId string, envId // RemoveAlias implements environments.Environments func (_d telemetryMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("RemoveAlias"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.RemoveAlias") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -155,6 +239,8 @@ func (_d telemetryMiddleware) RemoveAlias(ctx context.Context, spaceId string, e "alias": alias}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -167,8 +253,19 @@ func (_d telemetryMiddleware) RemoveAlias(ctx context.Context, spaceId string, e // SetAlias implements environments.Environments func (_d telemetryMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("SetAlias"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.SetAlias") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -177,6 +274,8 @@ func (_d telemetryMiddleware) SetAlias(ctx context.Context, spaceId string, envI "alias": alias}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -189,14 +288,27 @@ func (_d telemetryMiddleware) SetAlias(ctx context.Context, spaceId string, envI // Update implements environments.Environments func (_d telemetryMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Environments.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "env": env}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/files/middleware/telemetry_middleware.go b/pkg/files/middleware/telemetry_middleware.go index 21e10e8df709561cc9234df1a2621754c29ebe49..6a79a85ae49103cdd0994769d6999c88bc2a9767 100644 --- a/pkg/files/middleware/telemetry_middleware.go +++ b/pkg/files/middleware/telemetry_middleware.go @@ -8,10 +8,14 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/files" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { files.Files _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base files.Files, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base files.Files, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Files: base, - _instance: instance, + Files: base, + _instance: instance, + serviceName: strings.ToLower("Files"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,14 +46,27 @@ func TelemetryMiddleware(base files.Files, instance string, spanDecorator ...fun // AbortUpload implements files.Files func (_d telemetryMiddleware) AbortUpload(ctx context.Context, upload *files.MultipartUpload) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("AbortUpload"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Files.AbortUpload") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "upload": upload}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -58,8 +79,19 @@ func (_d telemetryMiddleware) AbortUpload(ctx context.Context, upload *files.Mul // CompleteUpload implements files.Files func (_d telemetryMiddleware) CompleteUpload(ctx context.Context, upload *files.MultipartUpload) (u *files.MultipartUpload, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("CompleteUpload"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Files.CompleteUpload") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -67,6 +99,8 @@ func (_d telemetryMiddleware) CompleteUpload(ctx context.Context, upload *files. "u": u, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -79,14 +113,27 @@ func (_d telemetryMiddleware) CompleteUpload(ctx context.Context, upload *files. // DeleteFile implements files.Files func (_d telemetryMiddleware) DeleteFile(ctx context.Context, file *files.File) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("DeleteFile"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Files.DeleteFile") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "file": file}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -99,8 +146,19 @@ func (_d telemetryMiddleware) DeleteFile(ctx context.Context, file *files.File) // GetFile implements files.Files func (_d telemetryMiddleware) GetFile(ctx context.Context, file *files.File) (f *files.File, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetFile"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Files.GetFile") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -108,6 +166,8 @@ func (_d telemetryMiddleware) GetFile(ctx context.Context, file *files.File) (f "f": f, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -120,8 +180,19 @@ func (_d telemetryMiddleware) GetFile(ctx context.Context, file *files.File) (f // MoveUpload implements files.Files func (_d telemetryMiddleware) MoveUpload(ctx context.Context, upload *files.MultipartUpload) (file *files.File, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("MoveUpload"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Files.MoveUpload") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -129,6 +200,8 @@ func (_d telemetryMiddleware) MoveUpload(ctx context.Context, upload *files.Mult "file": file, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -141,8 +214,19 @@ func (_d telemetryMiddleware) MoveUpload(ctx context.Context, upload *files.Mult // StartUpload implements files.Files func (_d telemetryMiddleware) StartUpload(ctx context.Context, upload *files.MultipartUpload) (u *files.MultipartUpload, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("StartUpload"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Files.StartUpload") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -150,6 +234,8 @@ func (_d telemetryMiddleware) StartUpload(ctx context.Context, upload *files.Mul "u": u, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -162,8 +248,19 @@ func (_d telemetryMiddleware) StartUpload(ctx context.Context, upload *files.Mul // Upload implements files.Files func (_d telemetryMiddleware) Upload(ctx context.Context, file *files.File) (u *files.Upload, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Upload"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Files.Upload") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -171,6 +268,8 @@ func (_d telemetryMiddleware) Upload(ctx context.Context, file *files.File) (u * "u": u, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/invitations/middleware/telemetry_middleware.go b/pkg/invitations/middleware/telemetry_middleware.go index cf79342a3ee7ef978c18b1d785ec40e4d47523bc..8144176fe0a73b87cd19655ac148aef76f32c0cb 100644 --- a/pkg/invitations/middleware/telemetry_middleware.go +++ b/pkg/invitations/middleware/telemetry_middleware.go @@ -8,11 +8,15 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/invitations" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/options" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -20,14 +24,18 @@ import ( type telemetryMiddleware struct { invitations.Invitations _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base invitations.Invitations, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base invitations.Invitations, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Invitations: base, - _instance: instance, + Invitations: base, + _instance: instance, + serviceName: strings.ToLower("Invitations"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -39,8 +47,19 @@ func TelemetryMiddleware(base invitations.Invitations, instance string, spanDeco // Accept implements invitations.Invitations func (_d telemetryMiddleware) Accept(ctx context.Context, invitationId string, userId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Accept"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Invitations.Accept") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -48,6 +67,8 @@ func (_d telemetryMiddleware) Accept(ctx context.Context, invitationId string, u "userId": userId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -60,8 +81,19 @@ func (_d telemetryMiddleware) Accept(ctx context.Context, invitationId string, u // Create implements invitations.Invitations func (_d telemetryMiddleware) Create(ctx context.Context, invitation *invitations.Invitation) (created *invitations.Invitation, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Invitations.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -69,6 +101,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, invitation *invitation "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -81,14 +115,27 @@ func (_d telemetryMiddleware) Create(ctx context.Context, invitation *invitation // Delete implements invitations.Invitations func (_d telemetryMiddleware) Delete(ctx context.Context, invitationId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Invitations.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "invitationId": invitationId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -101,8 +148,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, invitationId string) ( // Find implements invitations.Invitations func (_d telemetryMiddleware) Find(ctx context.Context, filter *invitations.Filter, opts *options.FindOptions) (invitations []*invitations.Invitation, total int, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Find"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Invitations.Find") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -112,6 +170,8 @@ func (_d telemetryMiddleware) Find(ctx context.Context, filter *invitations.Filt "total": total, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -124,8 +184,19 @@ func (_d telemetryMiddleware) Find(ctx context.Context, filter *invitations.Filt // Get implements invitations.Invitations func (_d telemetryMiddleware) Get(ctx context.Context, invitationId string) (invitation *invitations.Invitation, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Invitations.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -133,6 +204,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, invitationId string) (inv "invitation": invitation, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/items/middleware/telemetry_middleware.go b/pkg/items/middleware/telemetry_middleware.go index efbbc479d3b4ac1484d169376497aa20a1f034cc..b0d440d51ee52a22f0711c163504df2aa3fa5cc1 100644 --- a/pkg/items/middleware/telemetry_middleware.go +++ b/pkg/items/middleware/telemetry_middleware.go @@ -8,11 +8,15 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/items" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/schema" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -20,14 +24,18 @@ import ( type telemetryMiddleware struct { items.Items _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base items.Items, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base items.Items, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Items: base, - _instance: instance, + Items: base, + _instance: instance, + serviceName: strings.ToLower("Items"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -39,8 +47,19 @@ func TelemetryMiddleware(base items.Items, instance string, spanDecorator ...fun // Aggregate implements items.Items func (_d telemetryMiddleware) Aggregate(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregateOptions) (result map[string]interface{}, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Aggregate"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Aggregate") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -52,6 +71,8 @@ func (_d telemetryMiddleware) Aggregate(ctx context.Context, spaceId string, env "result": result, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -64,8 +85,19 @@ func (_d telemetryMiddleware) Aggregate(ctx context.Context, spaceId string, env // AggregatePublished implements items.Items func (_d telemetryMiddleware) AggregatePublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregatePublishedOptions) (result map[string]interface{}, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("AggregatePublished"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.AggregatePublished") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -77,6 +109,8 @@ func (_d telemetryMiddleware) AggregatePublished(ctx context.Context, spaceId st "result": result, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -89,8 +123,19 @@ func (_d telemetryMiddleware) AggregatePublished(ctx context.Context, spaceId st // Archive implements items.Items func (_d telemetryMiddleware) Archive(ctx context.Context, item *items.Item, options ...*items.ArchiveOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Archive"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Archive") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -98,6 +143,8 @@ func (_d telemetryMiddleware) Archive(ctx context.Context, item *items.Item, opt "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -110,8 +157,19 @@ func (_d telemetryMiddleware) Archive(ctx context.Context, item *items.Item, opt // Create implements items.Items func (_d telemetryMiddleware) Create(ctx context.Context, item *items.Item, opts ...*items.CreateOptions) (created *items.Item, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -120,6 +178,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, item *items.Item, opts "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -132,8 +192,19 @@ func (_d telemetryMiddleware) Create(ctx context.Context, item *items.Item, opts // Delete implements items.Items func (_d telemetryMiddleware) Delete(ctx context.Context, item *items.Item, options ...*items.DeleteOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -141,6 +212,8 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, item *items.Item, opti "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -153,8 +226,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, item *items.Item, opti // Find implements items.Items func (_d telemetryMiddleware) Find(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindOptions) (items []*items.Item, total int, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Find"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Find") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -167,6 +251,8 @@ func (_d telemetryMiddleware) Find(ctx context.Context, spaceId string, envId st "total": total, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -179,8 +265,19 @@ func (_d telemetryMiddleware) Find(ctx context.Context, spaceId string, envId st // FindArchived implements items.Items func (_d telemetryMiddleware) FindArchived(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindArchivedOptions) (items []*items.Item, total int, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("FindArchived"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.FindArchived") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -193,6 +290,8 @@ func (_d telemetryMiddleware) FindArchived(ctx context.Context, spaceId string, "total": total, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -205,8 +304,19 @@ func (_d telemetryMiddleware) FindArchived(ctx context.Context, spaceId string, // FindPublished implements items.Items func (_d telemetryMiddleware) FindPublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindPublishedOptions) (items []*items.Item, total int, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("FindPublished"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.FindPublished") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -219,6 +329,8 @@ func (_d telemetryMiddleware) FindPublished(ctx context.Context, spaceId string, "total": total, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -231,8 +343,19 @@ func (_d telemetryMiddleware) FindPublished(ctx context.Context, spaceId string, // Get implements items.Items func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetOptions) (item *items.Item, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -244,6 +367,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str "item": item, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -256,8 +381,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str // GetPublished implements items.Items func (_d telemetryMiddleware) GetPublished(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetPublishedOptions) (item *items.Item, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetPublished"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.GetPublished") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -269,6 +405,8 @@ func (_d telemetryMiddleware) GetPublished(ctx context.Context, spaceId string, "item": item, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -281,8 +419,19 @@ func (_d telemetryMiddleware) GetPublished(ctx context.Context, spaceId string, // GetRevision implements items.Items func (_d telemetryMiddleware) GetRevision(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, revisionId string, options ...*items.GetRevisionOptions) (item *items.Item, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetRevision"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.GetRevision") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -295,6 +444,8 @@ func (_d telemetryMiddleware) GetRevision(ctx context.Context, spaceId string, e "item": item, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -307,8 +458,19 @@ func (_d telemetryMiddleware) GetRevision(ctx context.Context, spaceId string, e // Introspect implements items.Items func (_d telemetryMiddleware) Introspect(ctx context.Context, item *items.Item, opts ...*items.IntrospectOptions) (itm *items.Item, sch *schema.Schema, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Introspect"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Introspect") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -318,6 +480,8 @@ func (_d telemetryMiddleware) Introspect(ctx context.Context, item *items.Item, "sch": sch, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -330,8 +494,19 @@ func (_d telemetryMiddleware) Introspect(ctx context.Context, item *items.Item, // ListRevisions implements items.Items func (_d telemetryMiddleware) ListRevisions(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.ListRevisionsOptions) (items []*items.Item, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListRevisions"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.ListRevisions") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -343,6 +518,8 @@ func (_d telemetryMiddleware) ListRevisions(ctx context.Context, spaceId string, "items": items, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -355,8 +532,19 @@ func (_d telemetryMiddleware) ListRevisions(ctx context.Context, spaceId string, // Publish implements items.Items func (_d telemetryMiddleware) Publish(ctx context.Context, item *items.Item, options ...*items.PublishOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Publish"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Publish") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -364,6 +552,8 @@ func (_d telemetryMiddleware) Publish(ctx context.Context, item *items.Item, opt "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -376,8 +566,19 @@ func (_d telemetryMiddleware) Publish(ctx context.Context, item *items.Item, opt // Unarchive implements items.Items func (_d telemetryMiddleware) Unarchive(ctx context.Context, item *items.Item, options ...*items.UnarchiveOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Unarchive"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Unarchive") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -385,6 +586,8 @@ func (_d telemetryMiddleware) Unarchive(ctx context.Context, item *items.Item, o "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -397,8 +600,19 @@ func (_d telemetryMiddleware) Unarchive(ctx context.Context, item *items.Item, o // Undelete implements items.Items func (_d telemetryMiddleware) Undelete(ctx context.Context, item *items.Item, options ...*items.UndeleteOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Undelete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Undelete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -406,6 +620,8 @@ func (_d telemetryMiddleware) Undelete(ctx context.Context, item *items.Item, op "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -418,8 +634,19 @@ func (_d telemetryMiddleware) Undelete(ctx context.Context, item *items.Item, op // Unpublish implements items.Items func (_d telemetryMiddleware) Unpublish(ctx context.Context, item *items.Item, options ...*items.UnpublishOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Unpublish"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Unpublish") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -427,6 +654,8 @@ func (_d telemetryMiddleware) Unpublish(ctx context.Context, item *items.Item, o "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -439,8 +668,19 @@ func (_d telemetryMiddleware) Unpublish(ctx context.Context, item *items.Item, o // Update implements items.Items func (_d telemetryMiddleware) Update(ctx context.Context, item *items.Item, options ...*items.UpdateOptions) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Items.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -448,6 +688,8 @@ func (_d telemetryMiddleware) Update(ctx context.Context, item *items.Item, opti "options": options}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/locales/middleware/telemetry_middleware.go b/pkg/locales/middleware/telemetry_middleware.go index 3e335e19574e238a02a69dc19f373c62e7fc65a2..2abc54ed32c9d821ef9100d8466487acd4e51b7f 100644 --- a/pkg/locales/middleware/telemetry_middleware.go +++ b/pkg/locales/middleware/telemetry_middleware.go @@ -8,10 +8,14 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/locales" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { locales.Locales _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base locales.Locales, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base locales.Locales, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Locales: base, - _instance: instance, + Locales: base, + _instance: instance, + serviceName: strings.ToLower("Locales"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,8 +46,19 @@ func TelemetryMiddleware(base locales.Locales, instance string, spanDecorator .. // Create implements locales.Locales func (_d telemetryMiddleware) Create(ctx context.Context, locale *locales.Locale) (created *locales.Locale, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Locales.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -47,6 +66,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, locale *locales.Locale "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -59,8 +80,19 @@ func (_d telemetryMiddleware) Create(ctx context.Context, locale *locales.Locale // Delete implements locales.Locales func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, localeId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Locales.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -68,6 +100,8 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, locale "localeId": localeId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -80,8 +114,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, locale // List implements locales.Locales func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (locales []*locales.Locale, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("List"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Locales.List") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -89,6 +134,8 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (locales "locales": locales, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/members/middleware/telemetry_middleware.go b/pkg/members/middleware/telemetry_middleware.go index 4d13a5d794a57fd9c470bae42bbfed4fe81743cc..40b0dcef276ef0513badd8e7241e353bb9b6f77a 100644 --- a/pkg/members/middleware/telemetry_middleware.go +++ b/pkg/members/middleware/telemetry_middleware.go @@ -8,10 +8,14 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/members" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { members.Members _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base members.Members, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base members.Members, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Members: base, - _instance: instance, + Members: base, + _instance: instance, + serviceName: strings.ToLower("Members"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,8 +46,19 @@ func TelemetryMiddleware(base members.Members, instance string, spanDecorator .. // Get implements members.Members func (_d telemetryMiddleware) Get(ctx context.Context, orgId string, userId string) (role members.Role, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Members.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -48,6 +67,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, orgId string, userId stri "role": role, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -60,8 +81,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, orgId string, userId stri // ListMembers implements members.Members func (_d telemetryMiddleware) ListMembers(ctx context.Context, orgId string) (members []*members.Member, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListMembers"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Members.ListMembers") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -69,6 +101,8 @@ func (_d telemetryMiddleware) ListMembers(ctx context.Context, orgId string) (me "members": members, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -81,8 +115,19 @@ func (_d telemetryMiddleware) ListMembers(ctx context.Context, orgId string) (me // ListOrganizations implements members.Members func (_d telemetryMiddleware) ListOrganizations(ctx context.Context, userId string) (organizations []*members.Member, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListOrganizations"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Members.ListOrganizations") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -90,6 +135,8 @@ func (_d telemetryMiddleware) ListOrganizations(ctx context.Context, userId stri "organizations": organizations, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -102,8 +149,19 @@ func (_d telemetryMiddleware) ListOrganizations(ctx context.Context, userId stri // Remove implements members.Members func (_d telemetryMiddleware) Remove(ctx context.Context, orgId string, userId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Remove"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Members.Remove") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -111,6 +169,8 @@ func (_d telemetryMiddleware) Remove(ctx context.Context, orgId string, userId s "userId": userId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -123,14 +183,27 @@ func (_d telemetryMiddleware) Remove(ctx context.Context, orgId string, userId s // RemoveAll implements members.Members func (_d telemetryMiddleware) RemoveAll(ctx context.Context, orgId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("RemoveAll"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Members.RemoveAll") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "orgId": orgId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -143,8 +216,19 @@ func (_d telemetryMiddleware) RemoveAll(ctx context.Context, orgId string) (err // Set implements members.Members func (_d telemetryMiddleware) Set(ctx context.Context, orgId string, userId string, role members.Role) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Set"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Members.Set") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -153,6 +237,8 @@ func (_d telemetryMiddleware) Set(ctx context.Context, orgId string, userId stri "role": role}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/organizations/middleware/telemetry_middleware.go b/pkg/organizations/middleware/telemetry_middleware.go index 6589ed10ba1ee502c6620f8cbef20ed6da22bb29..8e4a556e118d289d82b2847a21190891861b3dbc 100644 --- a/pkg/organizations/middleware/telemetry_middleware.go +++ b/pkg/organizations/middleware/telemetry_middleware.go @@ -8,11 +8,15 @@ package middleware import ( "context" + "strings" + "time" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/options" "git.perx.ru/perxis/perxis-go/pkg/organizations" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -20,14 +24,18 @@ import ( type telemetryMiddleware struct { organizations.Organizations _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base organizations.Organizations, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base organizations.Organizations, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Organizations: base, - _instance: instance, + Organizations: base, + _instance: instance, + serviceName: strings.ToLower("Organizations"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -39,8 +47,19 @@ func TelemetryMiddleware(base organizations.Organizations, instance string, span // Create implements organizations.Organizations func (_d telemetryMiddleware) Create(ctx context.Context, org *organizations.Organization) (created *organizations.Organization, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Organizations.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -48,6 +67,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, org *organizations.Org "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -60,14 +81,27 @@ func (_d telemetryMiddleware) Create(ctx context.Context, org *organizations.Org // Delete implements organizations.Organizations func (_d telemetryMiddleware) Delete(ctx context.Context, orgId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Organizations.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "orgId": orgId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -80,8 +114,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, orgId string) (err err // Find implements organizations.Organizations func (_d telemetryMiddleware) Find(ctx context.Context, filter *organizations.Filter, opts *options.FindOptions) (orgs []*organizations.Organization, total int, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Find"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Organizations.Find") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -91,6 +136,8 @@ func (_d telemetryMiddleware) Find(ctx context.Context, filter *organizations.Fi "total": total, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -103,8 +150,19 @@ func (_d telemetryMiddleware) Find(ctx context.Context, filter *organizations.Fi // Get implements organizations.Organizations func (_d telemetryMiddleware) Get(ctx context.Context, orgId string) (org *organizations.Organization, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Organizations.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -112,6 +170,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, orgId string) (org *organ "org": org, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -124,14 +184,27 @@ func (_d telemetryMiddleware) Get(ctx context.Context, orgId string) (org *organ // Update implements organizations.Organizations func (_d telemetryMiddleware) Update(ctx context.Context, org *organizations.Organization) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Organizations.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "org": org}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/references/middleware/telemetry_middleware.go b/pkg/references/middleware/telemetry_middleware.go index 16fe5d30d2e5cc8dc82adfc7d7a3db87acf45636..2af529824c43874061210449924cef0da5228e94 100644 --- a/pkg/references/middleware/telemetry_middleware.go +++ b/pkg/references/middleware/telemetry_middleware.go @@ -8,11 +8,15 @@ package middleware import ( "context" + "strings" + "time" "git.perx.ru/perxis/perxis-go/pkg/items" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/references" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -20,14 +24,18 @@ import ( type telemetryMiddleware struct { references.References _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base references.References, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base references.References, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - References: base, - _instance: instance, + References: base, + _instance: instance, + serviceName: strings.ToLower("References"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -39,8 +47,19 @@ func TelemetryMiddleware(base references.References, instance string, spanDecora // Get implements references.References func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference) (items []*items.Item, notfound []*references.Reference, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "References.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -51,6 +70,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str "notfound": notfound, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -63,8 +84,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str // Publish implements references.References func (_d telemetryMiddleware) Publish(ctx context.Context, spaceId string, envId string, references []*references.Reference, recursive bool, force bool) (published []*references.Reference, notfound []*references.Reference, unpublished []*references.Reference, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Publish"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "References.Publish") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -78,6 +110,8 @@ func (_d telemetryMiddleware) Publish(ctx context.Context, spaceId string, envId "unpublished": unpublished, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/roles/middleware/telemetry_middleware.go b/pkg/roles/middleware/telemetry_middleware.go index fe487489348a421c611c16b24c8fe689908dcaa8..b80a9dc24819fba965f711a4d0169ef4a2457396 100644 --- a/pkg/roles/middleware/telemetry_middleware.go +++ b/pkg/roles/middleware/telemetry_middleware.go @@ -8,10 +8,14 @@ package middleware import ( "context" + "strings" + "time" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/roles" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { roles.Roles _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base roles.Roles, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base roles.Roles, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Roles: base, - _instance: instance, + Roles: base, + _instance: instance, + serviceName: strings.ToLower("Roles"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,8 +46,19 @@ func TelemetryMiddleware(base roles.Roles, instance string, spanDecorator ...fun // Create implements roles.Roles func (_d telemetryMiddleware) Create(ctx context.Context, role *roles.Role) (created *roles.Role, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Roles.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -47,6 +66,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, role *roles.Role) (cre "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -59,8 +80,19 @@ func (_d telemetryMiddleware) Create(ctx context.Context, role *roles.Role) (cre // Delete implements roles.Roles func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, roleId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Roles.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -68,6 +100,8 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, roleId "roleId": roleId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -80,8 +114,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string, roleId // Get implements roles.Roles func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, roleId string) (role *roles.Role, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Roles.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -90,6 +135,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, roleId st "role": role, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -102,8 +149,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, roleId st // List implements roles.Roles func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (roles []*roles.Role, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("List"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Roles.List") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -111,6 +169,8 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (roles [ "roles": roles, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -123,14 +183,27 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (roles [ // Update implements roles.Roles func (_d telemetryMiddleware) Update(ctx context.Context, role *roles.Role) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Roles.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "role": role}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/spaces/middleware/telemetry_middleware.go b/pkg/spaces/middleware/telemetry_middleware.go index ba64829909edddc0ed5fbf100265552f611ae584..c15fff13c98ea98f120dc42fc71177b832d75f24 100644 --- a/pkg/spaces/middleware/telemetry_middleware.go +++ b/pkg/spaces/middleware/telemetry_middleware.go @@ -1,17 +1,21 @@ -package middleware - // Code generated by gowrap. DO NOT EDIT. // template: ../../../assets/templates/middleware/telemetry // gowrap: http://github.com/hexdigest/gowrap +package middleware + //go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/spaces -i Spaces -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l "" import ( "context" + "strings" + "time" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/spaces" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -19,14 +23,18 @@ import ( type telemetryMiddleware struct { spaces.Spaces _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base spaces.Spaces, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base spaces.Spaces, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Spaces: base, - _instance: instance, + Spaces: base, + _instance: instance, + serviceName: strings.ToLower("Spaces"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -38,14 +46,27 @@ func TelemetryMiddleware(base spaces.Spaces, instance string, spanDecorator ...f // AbortTransfer implements spaces.Spaces func (_d telemetryMiddleware) AbortTransfer(ctx context.Context, spaceID string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("AbortTransfer"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.AbortTransfer") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "spaceID": spaceID}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -58,8 +79,19 @@ func (_d telemetryMiddleware) AbortTransfer(ctx context.Context, spaceID string) // Create implements spaces.Spaces func (_d telemetryMiddleware) Create(ctx context.Context, space *spaces.Space) (created *spaces.Space, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -67,6 +99,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, space *spaces.Space) ( "created": created, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -79,14 +113,27 @@ func (_d telemetryMiddleware) Create(ctx context.Context, space *spaces.Space) ( // Delete implements spaces.Spaces func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "spaceId": spaceId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -99,8 +146,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string) (err e // Get implements spaces.Spaces func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string) (space *spaces.Space, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -108,6 +166,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string) (space *s "space": space, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -120,8 +180,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string) (space *s // List implements spaces.Spaces func (_d telemetryMiddleware) List(ctx context.Context, orgId string) (spaces []*spaces.Space, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("List"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.List") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -129,6 +200,8 @@ func (_d telemetryMiddleware) List(ctx context.Context, orgId string) (spaces [] "spaces": spaces, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -141,8 +214,19 @@ func (_d telemetryMiddleware) List(ctx context.Context, orgId string) (spaces [] // ListTransfers implements spaces.Spaces func (_d telemetryMiddleware) ListTransfers(ctx context.Context, orgID string) (spaces []*spaces.Space, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("ListTransfers"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.ListTransfers") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -150,6 +234,8 @@ func (_d telemetryMiddleware) ListTransfers(ctx context.Context, orgID string) ( "spaces": spaces, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -162,8 +248,19 @@ func (_d telemetryMiddleware) ListTransfers(ctx context.Context, orgID string) ( // Move implements spaces.Spaces func (_d telemetryMiddleware) Move(ctx context.Context, spaceID string, orgID string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Move"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.Move") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -171,6 +268,8 @@ func (_d telemetryMiddleware) Move(ctx context.Context, spaceID string, orgID st "orgID": orgID}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -183,8 +282,19 @@ func (_d telemetryMiddleware) Move(ctx context.Context, spaceID string, orgID st // Transfer implements spaces.Spaces func (_d telemetryMiddleware) Transfer(ctx context.Context, spaceID string, transferToOrg string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Transfer"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.Transfer") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -192,6 +302,8 @@ func (_d telemetryMiddleware) Transfer(ctx context.Context, spaceID string, tran "transferToOrg": transferToOrg}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -204,14 +316,27 @@ func (_d telemetryMiddleware) Transfer(ctx context.Context, spaceID string, tran // Update implements spaces.Spaces func (_d telemetryMiddleware) Update(ctx context.Context, space *spaces.Space) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "space": space}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -224,8 +349,19 @@ func (_d telemetryMiddleware) Update(ctx context.Context, space *spaces.Space) ( // UpdateConfig implements spaces.Spaces func (_d telemetryMiddleware) UpdateConfig(ctx context.Context, spaceId string, config *spaces.Config) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("UpdateConfig"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.UpdateConfig") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -233,6 +369,8 @@ func (_d telemetryMiddleware) UpdateConfig(ctx context.Context, spaceId string, "config": config}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) diff --git a/pkg/users/middleware/telemetry_middleware.go b/pkg/users/middleware/telemetry_middleware.go index a68b365d63d4c7668daf80035ca0f82d3d4bd598..7edba72cbaa514756322c4983e891788462cd585 100644 --- a/pkg/users/middleware/telemetry_middleware.go +++ b/pkg/users/middleware/telemetry_middleware.go @@ -8,11 +8,15 @@ package middleware import ( "context" + "strings" + "time" + "git.perx.ru/perxis/perxis-go/pkg/metrics" "git.perx.ru/perxis/perxis-go/pkg/options" "git.perx.ru/perxis/perxis-go/pkg/users" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" + metricotel "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" ) @@ -20,14 +24,18 @@ import ( type telemetryMiddleware struct { users.Users _instance string + serviceName string + requestMetrics *metrics.RequestMetrics _spanDecorator func(span trace.Span, params, results map[string]interface{}) } // TelemetryMiddleware returns telemetryMiddleware -func TelemetryMiddleware(base users.Users, instance string, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { +func TelemetryMiddleware(base users.Users, instance string, requestMetrics *metrics.RequestMetrics, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) telemetryMiddleware { d := telemetryMiddleware{ - Users: base, - _instance: instance, + Users: base, + _instance: instance, + serviceName: strings.ToLower("Users"), + requestMetrics: requestMetrics, } if len(spanDecorator) > 0 && spanDecorator[0] != nil { @@ -39,8 +47,19 @@ func TelemetryMiddleware(base users.Users, instance string, spanDecorator ...fun // Create implements users.Users func (_d telemetryMiddleware) Create(ctx context.Context, create *users.User) (user *users.User, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Create"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Users.Create") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -48,6 +67,8 @@ func (_d telemetryMiddleware) Create(ctx context.Context, create *users.User) (u "user": user, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -60,14 +81,27 @@ func (_d telemetryMiddleware) Create(ctx context.Context, create *users.User) (u // Delete implements users.Users func (_d telemetryMiddleware) Delete(ctx context.Context, userId string) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Delete"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Users.Delete") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "userId": userId}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -80,8 +114,19 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, userId string) (err er // Find implements users.Users func (_d telemetryMiddleware) Find(ctx context.Context, filter *users.Filter, options *options.FindOptions) (users []*users.User, total int, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Find"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Users.Find") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -91,6 +136,8 @@ func (_d telemetryMiddleware) Find(ctx context.Context, filter *users.Filter, op "total": total, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -103,8 +150,19 @@ func (_d telemetryMiddleware) Find(ctx context.Context, filter *users.Filter, op // Get implements users.Users func (_d telemetryMiddleware) Get(ctx context.Context, userId string) (user *users.User, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Get"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Users.Get") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -112,6 +170,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, userId string) (user *use "user": user, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -124,8 +184,19 @@ func (_d telemetryMiddleware) Get(ctx context.Context, userId string) (user *use // GetByIdentity implements users.Users func (_d telemetryMiddleware) GetByIdentity(ctx context.Context, identity string) (user *users.User, err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("GetByIdentity"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Users.GetByIdentity") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, @@ -133,6 +204,8 @@ func (_d telemetryMiddleware) GetByIdentity(ctx context.Context, identity string "user": user, "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error())) @@ -145,14 +218,27 @@ func (_d telemetryMiddleware) GetByIdentity(ctx context.Context, identity string // Update implements users.Users func (_d telemetryMiddleware) Update(ctx context.Context, update *users.User) (err error) { + attributes := metricotel.WithAttributes( + attribute.Key("service").String(_d.serviceName), + attribute.Key("method").String("Update"), + ) + + _d.requestMetrics.Total.Add(ctx, 1, attributes) + + start := time.Now() ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Users.Update") + defer func() { + _d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes) + if _d._spanDecorator != nil { _d._spanDecorator(_span, map[string]interface{}{ "ctx": ctx, "update": update}, map[string]interface{}{ "err": err}) } else if err != nil { + _d.requestMetrics.FailedTotal.Add(ctx, 1, attributes) + _span.RecordError(err) _span.SetAttributes(attribute.String("event", "error")) _span.SetAttributes(attribute.String("message", err.Error()))