Files
kurious/internal/common/decorator/logging.go
Aleksandr Trushkin 605e117586 add pagination
2024-04-07 23:49:06 +03:00

106 lines
2.5 KiB
Go

package decorator
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"strings"
"time"
"git.loyso.art/frx/kurious/internal/common/xcontext"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
var (
commandAttribute = attribute.Key("command_name")
queryAttribute = attribute.Key("query_name")
argsAttribute = attribute.Key("args")
apiTracer = otel.Tracer("cq")
)
type commandLoggingDecorator[T any] struct {
base CommandHandler[T]
log *slog.Logger
}
func (c commandLoggingDecorator[T]) Handle(ctx context.Context, cmd T) (err error) {
handlerName := getTypeName[T]()
ctx = xcontext.WithLogFields(ctx, slog.String("handler", handlerName))
var argsBuilder strings.Builder
_ = json.NewEncoder(&argsBuilder).Encode(cmd)
var span trace.Span
ctx, span = apiTracer.Start(ctx, handlerName)
span.SetAttributes(
commandAttribute.String(handlerName),
argsAttribute.String(argsBuilder.String()),
)
xcontext.LogDebug(ctx, c.log, "executing command")
start := time.Now()
defer func() {
elapsed := slog.Duration("elapsed", time.Since(start))
if err == nil {
xcontext.LogInfo(ctx, c.log, "command executed successfuly", elapsed)
} else {
xcontext.LogError(ctx, c.log, "command execution failed", elapsed, slog.Any("err", err))
span.RecordError(err)
}
span.End()
}()
return c.base.Handle(ctx, cmd)
}
type queryLoggingDecorator[Q, U any] struct {
base QueryHandler[Q, U]
log *slog.Logger
}
func (q queryLoggingDecorator[Q, U]) Handle(ctx context.Context, query Q) (entity U, err error) {
handlerName := getTypeName[Q]()
ctx = xcontext.WithLogFields(ctx, slog.String("handler", handlerName))
var argsBuilder strings.Builder
_ = json.NewEncoder(&argsBuilder).Encode(query)
var span trace.Span
ctx, span = apiTracer.Start(ctx, handlerName)
span.SetAttributes(
queryAttribute.String(handlerName),
argsAttribute.String(argsBuilder.String()),
)
xcontext.LogDebug(ctx, q.log, "executing command")
start := time.Now()
defer func() {
elapsed := slog.Duration("elapsed", time.Since(start))
if err == nil {
xcontext.LogInfo(ctx, q.log, "command executed successfuly", elapsed)
} else {
xcontext.LogError(ctx, q.log, "command execution failed", elapsed, slog.Any("err", err))
span.RecordError(err)
}
now := time.Now()
span.End(trace.WithTimestamp(now))
}()
return q.base.Handle(ctx, query)
}
func getTypeName[T any]() string {
var t T
out := fmt.Sprintf("%T", t)
return out
}