package core import ( "context" "fmt" "time" "github.com/rs/zerolog" ) type BaseAction interface{} type Action[Q, R any] interface { BaseAction Do(context.Context, Q) (R, error) } type ActionDecorator[Q, R any, A Action[Q, R]] interface { Action[Q, R] } type baseAction struct { env *Env } func newBaseAction(env *Env) baseAction { return baseAction{ env: env, } } func applyDecorators[Q, R any, A Action[Q, R]](action A) ActionDecorator[Q, R, A] { return logActionDecorator[Q, R, A]{ action: action, } } type logActionDecorator[Q, R any, A Action[Q, R]] struct { action Action[Q, R] } func (d logActionDecorator[Q, R, A]) Do(ctx context.Context, params Q) (result R, err error) { actionName := getTypeName[A]() start := time.Now() log := zerolog.Ctx(ctx).With().Str("action_name", actionName).Logger() ctx = log.WithContext(ctx) result, err = d.action.Do(ctx, params) elapsed := time.Since(start) if err != nil { log.Warn().Err(err).Dur("elapsed", elapsed).Msg("action failed") } log.Info().Dur("elapsed", elapsed).Msg("action successed") return result, err } func getTypeName[T any]() string { var t T out := fmt.Sprintf("%T", t) return out }