draincloud-core/internal/logger/builder.go

155 lines
2.8 KiB
Go
Raw Normal View History

2024-09-27 22:37:58 +00:00
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
}