package logging import ( "context" "fmt" "log/slog" "runtime" "time" ) // Logger creates a *slog.Logger wrap with a few more methods wrapped on top. // It recreates a number of methods to allow replacing a *slog.Logger functionally. // It also implements a number of methods to support formatted messages and // tracing. type Logger struct { *slog.Logger } func New(h slog.Handler) *Logger { return &Logger{ Logger: slog.New(h), } } func From(l *slog.Logger) *Logger { return &Logger{ Logger: l, } } func (l *Logger) With(args ...any) *Logger { return From(l.Logger.With(args...)) } func (l *Logger) WithGroup(name string) *Logger { return From(l.Logger.WithGroup(name)) } func (l *Logger) Debugf(format string, args ...any) { l.logContextf(context.Background(), slog.LevelDebug, format, args...) } func (l *Logger) DebugContextf(ctx context.Context, format string, args ...any) { l.logContextf(ctx, slog.LevelDebug, format, args...) } func (l *Logger) DebugDeferf(format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(context.Background(), slog.LevelDebug, format, args...) } func (l *Logger) DebugContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(ctx, slog.LevelDebug, format, args...) } func (l *Logger) DebugCallf(method string) func() { return l.logContextCallf(context.Background(), slog.LevelDebug, method) } func (l *Logger) DebugContextCallf(ctx context.Context, method string) func() { return l.logContextCallf(ctx, slog.LevelDebug, method) } func (l *Logger) Errorf(format string, args ...any) { l.logContextf(context.Background(), slog.LevelError, format, args...) } func (l *Logger) ErrorContextf(ctx context.Context, format string, args ...any) { l.logContextf(ctx, slog.LevelError, format, args...) } func (l *Logger) ErrorDeferf(format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(context.Background(), slog.LevelError, format, args...) } func (l *Logger) ErrorContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(ctx, slog.LevelError, format, args...) } func (l *Logger) ErrorCallf(method string) func() { return l.logContextCallf(context.Background(), slog.LevelError, method) } func (l *Logger) ErrorContextCallf(ctx context.Context, method string) func() { return l.logContextCallf(ctx, slog.LevelError, method) } func (l *Logger) Infof(format string, args ...any) { l.logContextf(context.Background(), slog.LevelInfo, format, args...) } func (l *Logger) InfoContextf(ctx context.Context, format string, args ...any) { l.logContextf(ctx, slog.LevelInfo, format, args...) } func (l *Logger) InfoDeferf(format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(context.Background(), slog.LevelInfo, format, args...) } func (l *Logger) InfoContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(ctx, slog.LevelInfo, format, args...) } func (l *Logger) InfoCallf(method string) func() { return l.logContextCallf(context.Background(), slog.LevelInfo, method) } func (l *Logger) InfoContextCallf(ctx context.Context, method string) func() { return l.logContextCallf(ctx, slog.LevelInfo, method) } func (l *Logger) Warnf(format string, args ...any) { l.logContextf(context.Background(), slog.LevelWarn, format, args...) } func (l *Logger) WarnContextf(ctx context.Context, format string, args ...any) { l.logContextf(ctx, slog.LevelWarn, format, args...) } func (l *Logger) WarnDeferf(format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(context.Background(), slog.LevelWarn, format, args...) } func (l *Logger) WarnContextDeferf(ctx context.Context, format string, args ...any) func(func() (string, []any)) { return l.logContextDeferf(ctx, slog.LevelWarn, format, args...) } func (l *Logger) WarnCallf(method string) func() { return l.logContextCallf(context.Background(), slog.LevelWarn, method) } func (l *Logger) WarnContextCallf(ctx context.Context, method string) func() { return l.logContextCallf(ctx, slog.LevelWarn, method) } func (l *Logger) logContextf(ctx context.Context, lvl slog.Level, format string, args ...any) { if !l.Enabled(ctx, slog.LevelInfo) { return } var pcs [1]uintptr runtime.Callers(3, pcs[:]) // skip [Callers, Infof] _ = l.Handler().Handle( ctx, slog.NewRecord(time.Now(), lvl, fmt.Sprintf(format, args...), pcs[0]), ) } func (l *Logger) logContextCallf(ctx context.Context, lvl slog.Level, method string) func() { fn := l.With("method", method).logContextDeferf(ctx, lvl, "%s called", method) return func() { fn(func() (string, []any) { return "%s returned", []any{method} }) } } func (l *Logger) logContextDeferf(ctx context.Context, lvl slog.Level, format string, args ...any) func(func() (msg string, args []any)) { if !l.Enabled(ctx, slog.LevelInfo) { return func(func() (string, []any)) { } } var pcs [1]uintptr runtime.Callers(3, pcs[:]) // skip [Callers, Infof] pc := pcs[0] _ = l.Handler().Handle(ctx, slog.NewRecord(time.Now(), lvl, fmt.Sprintf(format, args...), pc)) return func(deferred func() (newFormat string, moreArgs []any)) { if !l.Enabled(ctx, slog.LevelInfo) { return } if deferred != nil { format, args = deferred() } _ = l.Handler().Handle(ctx, slog.NewRecord(time.Now(), lvl, fmt.Sprintf(format, args...), pc)) } }