diff --git a/assets/templates/middleware/telemetry b/assets/templates/middleware/telemetry
index 3e893c09f161d2e832544f8bca625b6d63b3d990..f35cde213c9523050d863351757fb0ee560c73fe 100644
--- a/assets/templates/middleware/telemetry
+++ b/assets/templates/middleware/telemetry
@@ -1,8 +1,13 @@
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
+
 import (
     "context"
+    "time"
 
+    "git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
     "go.opentelemetry.io/otel"
     "go.opentelemetry.io/otel/attribute"
+    otelmetric "go.opentelemetry.io/otel/metric"
     "go.opentelemetry.io/otel/trace"
 )
 
@@ -13,14 +18,21 @@ import (
 type {{$decorator}} struct {
   {{.Interface.Type}}
   _instance 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}} {
+  requestMetrics, err := metrics.GetRequestMetrics()
+  if err != nil {
+    panic(err)
+  }
+
   d := {{$decorator}} {
     {{.Interface.Name}}: base,
     _instance: instance,
+    requestMetrics: requestMetrics,
   }
 
   if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -34,11 +46,24 @@ func {{$funcName}} (base {{.Interface.Type}}, instance string, spanDecorator ...
   {{if $method.AcceptsContext}}
     // {{$method.Name}} implements {{$.Interface.Type}}
 func (_d {{$decorator}}) {{$method.Declaration}} {
+  attributes := otelmetric.WithAttributeSet(attribute.NewSet(
+    attribute.String("service", "{{ $.Interface.Name }}"),
+    attribute.String("method", "{{ $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/go.mod b/go.mod
index fcffea8fcde51f29c2f1f78523643a4b9cdd0329..8cd52d3b3533422183a392bd755566f2090087d5 100644
--- a/go.mod
+++ b/go.mod
@@ -29,6 +29,11 @@ require (
 	gopkg.in/yaml.v3 v3.0.1
 )
 
+require (
+	github.com/kr/pretty v0.3.1 // indirect
+	github.com/rogpeppe/go-internal v1.10.0 // indirect
+)
+
 require (
 	cloud.google.com/go/compute v1.23.3 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
@@ -43,20 +48,18 @@ require (
 	github.com/gosimple/unidecode v1.0.1 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/klauspost/compress v1.17.3 // indirect
-	github.com/kr/pretty v0.3.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/montanaflynn/stats v0.7.1 // indirect
 	github.com/nats-io/nkeys v0.4.6 // indirect
 	github.com/nats-io/nuid v1.0.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/rogpeppe/go-internal v1.10.0 // indirect
 	github.com/stretchr/objx v0.5.1 // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/scram v1.1.2 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
-	go.opentelemetry.io/otel/metric v1.21.0 // indirect
+	go.opentelemetry.io/otel/metric v1.21.0
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/sync v0.5.0 // indirect
 	golang.org/x/sys v0.14.0 // indirect
diff --git a/images/middleware/telemetry_middleware.go b/images/middleware/telemetry_middleware.go
index 2c0aac8eeaa3507d16ee852fd47b4a46569bc827..72d38c2e1d91813383116398e5c41b9bb93004f4 100644
--- a/images/middleware/telemetry_middleware.go
+++ b/images/middleware/telemetry_middleware.go
@@ -1,18 +1,23 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// template: ..\..\assets\templates\middleware\telemetry
 // gowrap: http://github.com/hexdigest/gowrap
 
 package middleware
 
-//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/images -i Images -t ../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/images -i Images -t ..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/images"
 	"git.perx.ru/perxis/perxis-go/pkg/files"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -20,14 +25,21 @@ import (
 type telemetryMiddleware struct {
 	images.Images
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Images:    base,
-		_instance: instance,
+		Images:         base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -39,8 +51,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Images"),
+		attribute.String("method", "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 +72,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/cache/telemetry_middleware.go b/pkg/cache/telemetry_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..5c22fccef41c3068bfc4631fe8819dd39f31e43a
--- /dev/null
+++ b/pkg/cache/telemetry_middleware.go
@@ -0,0 +1,48 @@
+package cache
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
+	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
+)
+
+type telemetryMiddleware struct {
+	next         Cache
+	cacheMetrics *metrics.CacheMetrics
+	attributes   otelmetric.MeasurementOption
+}
+
+// TelemetryMiddleware возвращает обертку над кэшем, которая используется для отслеживания количества хитов и промахов в кэше.
+func TelemetryMiddleware(next Cache, keyValues ...attribute.KeyValue) Cache {
+	cacheMetrics, err := metrics.GetCacheMetrics()
+	if err != nil {
+		panic(err)
+	}
+
+	return &telemetryMiddleware{
+		next:         next,
+		cacheMetrics: cacheMetrics,
+		attributes:   otelmetric.WithAttributes(keyValues...),
+	}
+}
+
+func (c *telemetryMiddleware) Set(key, value any) error {
+	return c.next.Set(key, value)
+}
+
+func (c *telemetryMiddleware) Get(key any) (any, error) {
+	value, err := c.next.Get(key)
+	if err != nil {
+		c.cacheMetrics.MissesTotal.Add(context.TODO(), 1, c.attributes)
+		return nil, err
+	}
+	c.cacheMetrics.HitsTotal.Add(context.TODO(), 1, c.attributes)
+	return value, nil
+}
+
+func (c *telemetryMiddleware) Remove(key any) error {
+	c.cacheMetrics.InvalidatesTotal.Add(context.TODO(), 1, c.attributes)
+	return c.next.Remove(key)
+}
diff --git a/pkg/clients/middleware/telemetry_middleware.go b/pkg/clients/middleware/telemetry_middleware.go
index d8536b10fcac0a1f237c688fe9a8bde3db93fdcf..22c604f2d5be413681ccdb933eb5c33a61517cc6 100644
--- a/pkg/clients/middleware/telemetry_middleware.go
+++ b/pkg/clients/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/clients -i Clients -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/clients -i Clients -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	clients.Clients
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Clients:   base,
-		_instance: instance,
+		Clients:        base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,8 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Clients"),
+		attribute.String("method", "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 +70,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 +84,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Clients"),
+		attribute.String("method", "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 +104,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 +118,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Clients"),
+		attribute.String("method", "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 +139,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 +153,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Clients"),
+		attribute.String("method", "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 +174,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 +188,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Clients"),
+		attribute.String("method", "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 +209,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 +223,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Clients"),
+		attribute.String("method", "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 +243,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 +257,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Clients"),
+		attribute.String("method", "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..cd95016628ed34da1c6fdd5c951a14c766a8cabc 100644
--- a/pkg/collaborators/middleware/telemetry_middleware.go
+++ b/pkg/collaborators/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/collaborators -i Collaborators -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/collaborators -i Collaborators -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collaborators"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	collaborators.Collaborators
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Collaborators: base,
-		_instance:     instance,
+		Collaborators:  base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,8 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collaborators"),
+		attribute.String("method", "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 +71,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 +85,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collaborators"),
+		attribute.String("method", "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 +105,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 +119,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collaborators"),
+		attribute.String("method", "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 +139,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 +153,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collaborators"),
+		attribute.String("method", "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 +173,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 +187,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collaborators"),
+		attribute.String("method", "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 +208,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..306570dba01ac1d7faaf193d92f97820369a6ad2 100644
--- a/pkg/collections/middleware/telemetry_middleware.go
+++ b/pkg/collections/middleware/telemetry_middleware.go
@@ -1,18 +1,23 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/collections -i Collections -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/collections -i Collections -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -20,14 +25,21 @@ import (
 type telemetryMiddleware struct {
 	collections.Collections
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Collections: base,
-		_instance:   instance,
+		Collections:    base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -39,8 +51,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collections"),
+		attribute.String("method", "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 +71,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 +85,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collections"),
+		attribute.String("method", "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 +106,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 +120,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collections"),
+		attribute.String("method", "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 +143,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 +157,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collections"),
+		attribute.String("method", "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 +179,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 +193,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collections"),
+		attribute.String("method", "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 +215,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 +229,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collections"),
+		attribute.String("method", "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 +251,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 +265,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Collections"),
+		attribute.String("method", "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..05a017300c0b2d60c0cc03df333c62365babffa6 100644
--- a/pkg/delivery/middleware/telemetry_middleware.go
+++ b/pkg/delivery/middleware/telemetry_middleware.go
@@ -1,21 +1,26 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/delivery -i Delivery -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/delivery -i Delivery -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"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/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -23,14 +28,21 @@ import (
 type telemetryMiddleware struct {
 	delivery.Delivery
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Delivery:  base,
-		_instance: instance,
+		Delivery:       base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -42,8 +54,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +78,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 +92,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +117,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 +131,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +153,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 +167,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +188,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 +202,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +226,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 +240,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +261,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 +275,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +295,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 +309,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Delivery"),
+		attribute.String("method", "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 +329,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..95b50dbc590f134386747a2fb4810455e7f2a800 100644
--- a/pkg/environments/middleware/telemetry_middleware.go
+++ b/pkg/environments/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/environments -i Environments -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	environments.Environments
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Environments: base,
-		_instance:    instance,
+		Environments:   base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,8 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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 +70,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 +84,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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 +104,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 +118,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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 +139,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 +153,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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 +173,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 +187,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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 +208,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 +222,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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 +243,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 +257,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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 +278,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 +292,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Environments"),
+		attribute.String("method", "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..bed97ea15eba2acd026249d698195835ee6a9db1 100644
--- a/pkg/files/middleware/telemetry_middleware.go
+++ b/pkg/files/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/files -i Files -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/files -i Files -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/files"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	files.Files
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Files:     base,
-		_instance: instance,
+		Files:          base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,14 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Files"),
+		attribute.String("method", "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 +83,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Files"),
+		attribute.String("method", "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 +103,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 +117,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Files"),
+		attribute.String("method", "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 +150,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Files"),
+		attribute.String("method", "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 +170,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 +184,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Files"),
+		attribute.String("method", "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 +204,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 +218,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Files"),
+		attribute.String("method", "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 +238,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 +252,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Files"),
+		attribute.String("method", "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 +272,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..44b7d2683137884f330946cce8d39a0eb3617d15 100644
--- a/pkg/invitations/middleware/telemetry_middleware.go
+++ b/pkg/invitations/middleware/telemetry_middleware.go
@@ -1,18 +1,23 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/invitations -i Invitations -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/invitations -i Invitations -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/invitations"
 	"git.perx.ru/perxis/perxis-go/pkg/options"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -20,14 +25,21 @@ import (
 type telemetryMiddleware struct {
 	invitations.Invitations
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Invitations: base,
-		_instance:   instance,
+		Invitations:    base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -39,8 +51,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Invitations"),
+		attribute.String("method", "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 +71,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 +85,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Invitations"),
+		attribute.String("method", "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 +105,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 +119,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Invitations"),
+		attribute.String("method", "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 +152,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Invitations"),
+		attribute.String("method", "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 +174,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 +188,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Invitations"),
+		attribute.String("method", "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 +208,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..98891f9f4f957ffe12626150c58e8bb7df67389a 100644
--- a/pkg/items/middleware/telemetry_middleware.go
+++ b/pkg/items/middleware/telemetry_middleware.go
@@ -1,18 +1,23 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/items -i Items -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/items -i Items -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/items"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -20,14 +25,21 @@ import (
 type telemetryMiddleware struct {
 	items.Items
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Items:     base,
-		_instance: instance,
+		Items:          base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -39,8 +51,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +75,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 +89,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +113,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 +127,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +147,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 +161,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +182,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 +196,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +216,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 +230,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +255,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 +269,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +294,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 +308,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +333,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 +347,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +371,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 +385,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +409,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 +423,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +448,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 +462,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +484,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 +498,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +522,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 +536,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +556,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 +570,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +590,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 +604,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +624,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 +638,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +658,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 +672,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Items"),
+		attribute.String("method", "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 +692,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..4efc5293b78781aaa5d8cbf77f4da82c714a6137 100644
--- a/pkg/locales/middleware/telemetry_middleware.go
+++ b/pkg/locales/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/locales -i Locales -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/locales -i Locales -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	locales.Locales
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Locales:   base,
-		_instance: instance,
+		Locales:        base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,8 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Locales"),
+		attribute.String("method", "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 +70,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 +84,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Locales"),
+		attribute.String("method", "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 +104,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 +118,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Locales"),
+		attribute.String("method", "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 +138,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..6193a4bacb41dcea61a1384e939bcdb2785f5e8a 100644
--- a/pkg/members/middleware/telemetry_middleware.go
+++ b/pkg/members/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/members -i Members -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/members -i Members -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/members"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	members.Members
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Members:   base,
-		_instance: instance,
+		Members:        base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,8 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Members"),
+		attribute.String("method", "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 +71,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 +85,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Members"),
+		attribute.String("method", "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 +105,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 +119,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Members"),
+		attribute.String("method", "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 +139,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 +153,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Members"),
+		attribute.String("method", "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 +173,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 +187,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Members"),
+		attribute.String("method", "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 +220,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Members"),
+		attribute.String("method", "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 +241,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/optional/optional.go b/pkg/optional/optional.go
index 94e89bf6a04708abf853f2e8aaf8d7dbd9e99371..b33d76b98a94b20ca357d6e42d2658ac389ee883 100644
--- a/pkg/optional/optional.go
+++ b/pkg/optional/optional.go
@@ -8,3 +8,7 @@ var (
 func Bool(v bool) *bool {
 	return &v
 }
+
+func Ptr[T any](v T) *T {
+	return &v
+}
diff --git a/pkg/organizations/middleware/telemetry_middleware.go b/pkg/organizations/middleware/telemetry_middleware.go
index 6589ed10ba1ee502c6620f8cbef20ed6da22bb29..d728b1f10f4ba303b6469d3a1b8bf63cb24a7283 100644
--- a/pkg/organizations/middleware/telemetry_middleware.go
+++ b/pkg/organizations/middleware/telemetry_middleware.go
@@ -1,18 +1,23 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/organizations -i Organizations -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/organizations -i Organizations -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/options"
 	"git.perx.ru/perxis/perxis-go/pkg/organizations"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -20,14 +25,21 @@ import (
 type telemetryMiddleware struct {
 	organizations.Organizations
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Organizations: base,
-		_instance:     instance,
+		Organizations:  base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -39,8 +51,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Organizations"),
+		attribute.String("method", "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 +71,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 +85,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Organizations"),
+		attribute.String("method", "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 +118,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Organizations"),
+		attribute.String("method", "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 +140,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 +154,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Organizations"),
+		attribute.String("method", "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 +174,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 +188,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Organizations"),
+		attribute.String("method", "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..f188bdc7ee04c13690ac0712e511508ccb2b12bc 100644
--- a/pkg/references/middleware/telemetry_middleware.go
+++ b/pkg/references/middleware/telemetry_middleware.go
@@ -1,18 +1,23 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/references -i References -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/references -i References -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/items"
 	"git.perx.ru/perxis/perxis-go/pkg/references"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -20,14 +25,21 @@ import (
 type telemetryMiddleware struct {
 	references.References
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		References: base,
-		_instance:  instance,
+		References:     base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -39,8 +51,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "References"),
+		attribute.String("method", "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 +74,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 +88,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "References"),
+		attribute.String("method", "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 +114,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..5d9d5e35cee66af5e24fb5888d52907ce35aadde 100644
--- a/pkg/roles/middleware/telemetry_middleware.go
+++ b/pkg/roles/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/roles -i Roles -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/roles -i Roles -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	roles.Roles
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Roles:     base,
-		_instance: instance,
+		Roles:          base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,8 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Roles"),
+		attribute.String("method", "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 +70,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 +84,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Roles"),
+		attribute.String("method", "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 +104,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 +118,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Roles"),
+		attribute.String("method", "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 +139,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 +153,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Roles"),
+		attribute.String("method", "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 +173,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 +187,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Roles"),
+		attribute.String("method", "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..90597815e8e7aa0a3a364c3e29aa208f3473d690 100644
--- a/pkg/spaces/middleware/telemetry_middleware.go
+++ b/pkg/spaces/middleware/telemetry_middleware.go
@@ -1,17 +1,22 @@
-package middleware
-
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// template: ..\..\..\assets\templates\middleware\telemetry
 // gowrap: http://github.com/hexdigest/gowrap
 
-//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 ""
+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 ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -19,14 +24,21 @@ import (
 type telemetryMiddleware struct {
 	spaces.Spaces
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Spaces:    base,
-		_instance: instance,
+		Spaces:         base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -38,14 +50,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +83,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +103,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 +117,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +150,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +170,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 +184,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +204,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 +218,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +238,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 +252,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +272,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 +286,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +306,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 +320,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +353,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "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 +373,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/telemetry/metrics/cache.go b/pkg/telemetry/metrics/cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..323fd26b840977b71b28dfcc1575f9479ed42013
--- /dev/null
+++ b/pkg/telemetry/metrics/cache.go
@@ -0,0 +1,40 @@
+package metrics
+
+import (
+	"go.opentelemetry.io/otel"
+	otelmetric "go.opentelemetry.io/otel/metric"
+)
+
+type CacheMetrics struct {
+	HitsTotal        otelmetric.Int64Counter
+	MissesTotal      otelmetric.Int64Counter
+	InvalidatesTotal otelmetric.Int64Counter
+}
+
+func GetCacheMetrics() (*CacheMetrics, error) {
+	var (
+		meter        = otel.Meter("")
+		cacheMetrics = new(CacheMetrics)
+		err          error
+	)
+
+	if cacheMetrics.HitsTotal, err = meter.Int64Counter("cache_hits",
+		otelmetric.WithDescription("Количество найденных в кэше значений"),
+	); err != nil {
+		return nil, err
+	}
+
+	if cacheMetrics.MissesTotal, err = meter.Int64Counter("cache_misses",
+		otelmetric.WithDescription("Количество не найденных в кэше значений"),
+	); err != nil {
+		return nil, err
+	}
+
+	if cacheMetrics.InvalidatesTotal, err = meter.Int64Counter("cache_invalidates",
+		otelmetric.WithDescription("Количество инвалидаций кэша"),
+	); err != nil {
+		return nil, err
+	}
+
+	return cacheMetrics, nil
+}
diff --git a/pkg/telemetry/metrics/request.go b/pkg/telemetry/metrics/request.go
new file mode 100644
index 0000000000000000000000000000000000000000..58da49785b9cc97083c407bcbf45ef32a82f36c4
--- /dev/null
+++ b/pkg/telemetry/metrics/request.go
@@ -0,0 +1,44 @@
+package metrics
+
+import (
+	"go.opentelemetry.io/otel"
+	otelmetric "go.opentelemetry.io/otel/metric"
+)
+
+type RequestMetrics struct {
+	Total                otelmetric.Int64Counter
+	FailedTotal          otelmetric.Int64Counter
+	DurationMilliseconds otelmetric.Int64Histogram
+}
+
+// GetRequestMetrics возвращает метрики для подсчета количества удачных/неудачных запросов, а так же длительности ответов.
+//
+// Для RequestMetrics.DurationMilliseconds значения buckets по умолчанию равно []float64{0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000}
+func GetRequestMetrics() (*RequestMetrics, error) {
+	var (
+		meter          = otel.Meter("")
+		requestMetrics = new(RequestMetrics)
+		err            error
+	)
+
+	if requestMetrics.Total, err = meter.Int64Counter("requests",
+		otelmetric.WithDescription("Количество запросов"),
+	); err != nil {
+		return nil, err
+	}
+
+	if requestMetrics.FailedTotal, err = meter.Int64Counter("requests_failed",
+		otelmetric.WithDescription("Количество запросов, вернувших ошибку"),
+	); err != nil {
+		return nil, err
+	}
+
+	if requestMetrics.DurationMilliseconds, err = meter.Int64Histogram("request_duration",
+		otelmetric.WithDescription("Длительность обработки запроса"),
+		otelmetric.WithUnit("ms"),
+	); err != nil {
+		return nil, err
+	}
+
+	return requestMetrics, nil
+}
diff --git a/pkg/users/middleware/telemetry_middleware.go b/pkg/users/middleware/telemetry_middleware.go
index a68b365d63d4c7668daf80035ca0f82d3d4bd598..698b4f6b4f2928e5f466e7855defa4c2df96ef36 100644
--- a/pkg/users/middleware/telemetry_middleware.go
+++ b/pkg/users/middleware/telemetry_middleware.go
@@ -1,18 +1,23 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ../../../assets/templates/middleware/telemetry
+// 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/users -i Users -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/users -i Users -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+
+// source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
 import (
 	"context"
+	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/options"
+	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"git.perx.ru/perxis/perxis-go/pkg/users"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
+	otelmetric "go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 )
 
@@ -20,14 +25,21 @@ import (
 type telemetryMiddleware struct {
 	users.Users
 	_instance      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 {
+	requestMetrics, err := metrics.GetRequestMetrics()
+	if err != nil {
+		panic(err)
+	}
+
 	d := telemetryMiddleware{
-		Users:     base,
-		_instance: instance,
+		Users:          base,
+		_instance:      instance,
+		requestMetrics: requestMetrics,
 	}
 
 	if len(spanDecorator) > 0 && spanDecorator[0] != nil {
@@ -39,8 +51,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Users"),
+		attribute.String("method", "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 +71,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 +85,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Users"),
+		attribute.String("method", "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 +118,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Users"),
+		attribute.String("method", "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 +140,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 +154,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Users"),
+		attribute.String("method", "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 +174,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 +188,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Users"),
+		attribute.String("method", "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 +208,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 +222,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 := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Users"),
+		attribute.String("method", "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()))