package logger import ( "context" "io" "log/slog" "os" "strings" ) type _key string //nolint:gochecknoglobals // ... var loggerKey _key = "_core_logger" type LoggerOpt func(p *loggerParams) func NewLoggerContext(ctx context.Context, opts ...LoggerOpt) context.Context { p := new(loggerParams) for _, o := range opts { o(p) } log := p.build() return context.WithValue(ctx, loggerKey, log) } type loggerParams struct { local bool addSource bool lvl slog.Level writers []io.Writer } func WithWriter(w io.Writer) LoggerOpt { return func(p *loggerParams) { p.writers = append(p.writers, w) } } func WithLevel(l slog.Level) LoggerOpt { return func(p *loggerParams) { p.lvl = l } } func Local() LoggerOpt { return func(p *loggerParams) { p.local = true } } func WithSource() LoggerOpt { return func(p *loggerParams) { p.addSource = true } } func Err(err error) slog.Attr { return slog.Attr{ Key: "error", Value: slog.StringValue(err.Error()), } } func MapLevel(lvl string) slog.Level { switch strings.ToLower(lvl) { case "debug": return LevelDebug case "info": return LevelInfo case "notice": return LevelNotice case "warn": return LevelWarn case "error": return LevelError case "critical": return LevelCritial case "alert": return LevelAlert case "emergency": return LevelEmergency default: return LevelInfo } } func (b *loggerParams) build() *slog.Logger { if len(b.writers) == 0 { b.writers = append(b.writers, os.Stdout) } w := io.MultiWriter(b.writers...) if b.local { opts := prettyHandlerOptions{ SlogOpts: &slog.HandlerOptions{ Level: b.lvl, AddSource: b.addSource, }, } handler := opts.newPrettyHandler(w) return slog.New(handler) } return newLogger(b.lvl, w) } func newLogger(lvl slog.Level, w io.Writer) *slog.Logger { return slog.New( slog.NewJSONHandler(w, &slog.HandlerOptions{ Level: lvl, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.LevelKey { level := a.Value.Any().(slog.Level) switch { case level < LevelInfo: a.Value = slog.StringValue("DEBUG") case level < LevelNotice: a.Value = slog.StringValue("INFO") case level < LevelWarn: a.Value = slog.StringValue("NOTICE") case level < LevelError: a.Value = slog.StringValue("WARNING") case level < LevelCritial: a.Value = slog.StringValue("ERROR") case level < LevelAlert: a.Value = slog.StringValue("CRITICAL") case level < LevelEmergency: a.Value = slog.StringValue("ALERT") default: a.Value = slog.StringValue("EMERGENCY") } } return a }, }), ) } func loggerFromCtx(ctx context.Context) *slog.Logger { if l, ok := ctx.Value(loggerKey).(*slog.Logger); ok { return l } return globalLogger }