diff --git a/log/middleware/error_logging_middleware.go b/log/middleware/error_logging_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..de13dcc4fa1d3f8edde99896cdfc24fe1a728128 --- /dev/null +++ b/log/middleware/error_logging_middleware.go @@ -0,0 +1,61 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../assets/templates/middleware/error_log +// gowrap: http://github.com/hexdigest/gowrap + +package middleware + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/log -i Service -t ../../assets/templates/middleware/error_log -o error_logging_middleware.go -l "" + +import ( + "context" + + "git.perx.ru/perxis/perxis-go/log" + "git.perx.ru/perxis/perxis-go/pkg/options" + "go.uber.org/zap" +) + +// errorLoggingMiddleware implements log.Service that is instrumented with logging +type errorLoggingMiddleware struct { + logger *zap.Logger + next log.Service +} + +// ErrorLoggingMiddleware instruments an implementation of the log.Service with simple logging +func ErrorLoggingMiddleware(logger *zap.Logger) Middleware { + return func(next log.Service) log.Service { + return &errorLoggingMiddleware{ + next: next, + logger: logger, + } + } +} + +func (m *errorLoggingMiddleware) Delete(ctx context.Context, filter *log.Filter) (err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Delete(ctx, filter) +} + +func (m *errorLoggingMiddleware) Find(ctx context.Context, filter *log.Filter, options *options.FindOptions) (fp1 *log.FindResult, err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Find(ctx, filter, options) +} + +func (m *errorLoggingMiddleware) Log(ctx context.Context, entries []*log.Entry) (err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Log(ctx, entries) +} diff --git a/log/middleware/logging_middleware.go b/log/middleware/logging_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..f693795ba6b45bc915212f968c6347a0aa7aed61 --- /dev/null +++ b/log/middleware/logging_middleware.go @@ -0,0 +1,145 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../assets/templates/middleware/access_log +// gowrap: http://github.com/hexdigest/gowrap + +package middleware + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/log -i Service -t ../../assets/templates/middleware/access_log -o logging_middleware.go -l "" + +import ( + "context" + "fmt" + "time" + + "git.perx.ru/perxis/perxis-go/log" + "git.perx.ru/perxis/perxis-go/pkg/auth" + "git.perx.ru/perxis/perxis-go/pkg/options" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// loggingMiddleware implements log.Service that is instrumented with logging +type loggingMiddleware struct { + logger *zap.Logger + next log.Service +} + +// LoggingMiddleware instruments an implementation of the log.Service with simple logging +func LoggingMiddleware(logger *zap.Logger) Middleware { + return func(next log.Service) log.Service { + return &loggingMiddleware{ + next: next, + logger: logger, + } + } +} + +func (m *loggingMiddleware) Delete(ctx context.Context, filter *log.Filter) (err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "filter": filter} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Delete.Request", fields...) + + err = m.next.Delete(ctx, filter) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + } + + for k, v := range map[string]interface{}{ + "err": err} { + if k == "err" { + err, _ := v.(error) + fields = append(fields, zap.Error(err)) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Delete.Response", fields...) + + return err +} + +func (m *loggingMiddleware) Find(ctx context.Context, filter *log.Filter, options *options.FindOptions) (fp1 *log.FindResult, err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "filter": filter, + "options": options} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Find.Request", fields...) + + fp1, err = m.next.Find(ctx, filter, options) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + } + + for k, v := range map[string]interface{}{ + "fp1": fp1, + "err": err} { + if k == "err" { + err, _ := v.(error) + fields = append(fields, zap.Error(err)) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Find.Response", fields...) + + return fp1, err +} + +func (m *loggingMiddleware) Log(ctx context.Context, entries []*log.Entry) (err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "entries": entries} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Log.Request", fields...) + + err = m.next.Log(ctx, entries) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + } + + for k, v := range map[string]interface{}{ + "err": err} { + if k == "err" { + err, _ := v.(error) + fields = append(fields, zap.Error(err)) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Log.Response", fields...) + + return err +} diff --git a/log/middleware/middleware.go b/log/middleware/middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..2fa83f77f251603392126319e61e27158b0dc3cc --- /dev/null +++ b/log/middleware/middleware.go @@ -0,0 +1,28 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../assets/templates/middleware/middleware +// gowrap: http://github.com/hexdigest/gowrap + +package middleware + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/log -i Service -t ../../assets/templates/middleware/middleware -o middleware.go -l "" + +import ( + "git.perx.ru/perxis/perxis-go/log" + "go.uber.org/zap" +) + +type Middleware func(log.Service) log.Service + +func WithLog(s log.Service, logger *zap.Logger, log_access bool) log.Service { + if logger == nil { + logger = zap.NewNop() + } + + logger = logger.Named("Service") + s = ErrorLoggingMiddleware(logger)(s) + if log_access { + s = LoggingMiddleware(logger)(s) + } + s = RecoveringMiddleware(logger)(s) + return s +} diff --git a/log/middleware/recovering_middleware.go b/log/middleware/recovering_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..b8b3de39570f3fdece8969edb24c8f4a9dbcb0cb --- /dev/null +++ b/log/middleware/recovering_middleware.go @@ -0,0 +1,68 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../assets/templates/middleware/recovery +// gowrap: http://github.com/hexdigest/gowrap + +package middleware + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/log -i Service -t ../../assets/templates/middleware/recovery -o recovering_middleware.go -l "" + +import ( + "context" + "fmt" + + "git.perx.ru/perxis/perxis-go/log" + "git.perx.ru/perxis/perxis-go/pkg/options" + "go.uber.org/zap" +) + +// recoveringMiddleware implements log.Service that is instrumented with logging +type recoveringMiddleware struct { + logger *zap.Logger + next log.Service +} + +// RecoveringMiddleware instruments an implementation of the log.Service with simple logging +func RecoveringMiddleware(logger *zap.Logger) Middleware { + return func(next log.Service) log.Service { + return &recoveringMiddleware{ + next: next, + logger: logger, + } + } +} + +func (m *recoveringMiddleware) Delete(ctx context.Context, filter *log.Filter) (err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Delete(ctx, filter) +} + +func (m *recoveringMiddleware) Find(ctx context.Context, filter *log.Filter, options *options.FindOptions) (fp1 *log.FindResult, err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Find(ctx, filter, options) +} + +func (m *recoveringMiddleware) Log(ctx context.Context, entries []*log.Entry) (err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Log(ctx, entries) +} diff --git a/log/middleware/telemetry_middleware.go b/log/middleware/telemetry_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..f5ba0676cf727ae7d70809b789002b61afe70bf3 --- /dev/null +++ b/log/middleware/telemetry_middleware.go @@ -0,0 +1,221 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ..\..\..\assets\templates\middleware\telemetry +// gowrap: http://github.com/hexdigest/gowrap + +package middleware + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/collaborators -i log -i Service -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" +) + +// telemetryMiddleware implements collaborators.Collaborators interface instrumented with opentracing spans +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, + requestMetrics: requestMetrics, + } + + if len(spanDecorator) > 0 && spanDecorator[0] != nil { + d._spanDecorator = spanDecorator[0] + } + + return d +} + +// 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, + "spaceId": spaceId, + "subject": subject}, map[string]interface{}{ + "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())) + } + + _span.End() + }() + return _d.Collaborators.Get(ctx, spaceId, subject) +} + +// 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, + "spaceId": spaceId}, map[string]interface{}{ + "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())) + } + + _span.End() + }() + return _d.Collaborators.ListCollaborators(ctx, spaceId) +} + +// 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, + "subject": subject}, map[string]interface{}{ + "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())) + } + + _span.End() + }() + return _d.Collaborators.ListSpaces(ctx, subject) +} + +// 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, + "spaceId": spaceId, + "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())) + } + + _span.End() + }() + return _d.Collaborators.Remove(ctx, spaceId, subject) +} + +// 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, + "spaceId": spaceId, + "subject": subject, + "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())) + } + + _span.End() + }() + return _d.Collaborators.Set(ctx, spaceId, subject, role) +}