package log
import (
"cmp"
"log/slog"
"os"
"strings"
"time"
"gopkg.awl.red/bizdex/config"
)
const (
defaultLevelOverrides = "D|I|W|E"
defaultLevelKey = "lvl"
defaultTimeFormat = "unix"
defaultTimeKey = "@"
)
func mkReplaceLogAttrs(
levelKey string,
levelOverrides []string,
timeFormat string,
timeKey string,
) func(groups []string, a slog.Attr) slog.Attr {
return func(groups []string, a slog.Attr) slog.Attr {
timeFormat := cmp.Or(timeFormat, defaultTimeFormat)
timeKey := cmp.Or(timeKey, defaultTimeKey)
if a.Key == slog.TimeKey && len(groups) == 0 {
logTime := a.Value.Any().(time.Time).UTC()
switch timeFormat {
case "unix":
return slog.Int64(timeKey, logTime.Unix())
case "unixmilli":
return slog.Int64(timeKey, logTime.UnixMilli())
default:
return slog.String(timeKey, logTime.Format(timeFormat))
}
}
if a.Key == slog.LevelKey && len(groups) == 0 {
lvl := a.Value.Any().(slog.Level)
lvlStr := lvl.String()
switch lvl {
case slog.LevelDebug:
lvlStr = levelOverrides[0]
case slog.LevelInfo:
lvlStr = levelOverrides[1]
case slog.LevelWarn:
lvlStr = levelOverrides[2]
case slog.LevelError:
lvlStr = levelOverrides[3]
}
return slog.String(levelKey, lvlStr)
}
return a
}
}
func New(lvl slog.Leveler, addSource bool, levelKey string, levelOverrides string, timeFormat string, timeKey string, setDefault bool) *slog.Logger {
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
ReplaceAttr: mkReplaceLogAttrs(
cmp.Or(levelKey, defaultLevelKey),
strings.Split(cmp.Or(levelOverrides, defaultLevelOverrides), "|"),
timeFormat,
timeKey,
),
AddSource: addSource,
Level: lvl,
}))
if setDefault {
slog.SetDefault(logger)
}
return logger
}
func SetFromConfig(cfg *config.Config) {
var leveler slog.Level
err := leveler.UnmarshalText([]byte(cfg.LogLevel))
if err != nil {
slog.Error("error unmarshalling level from text", "level", cfg.LogLevel)
}
leveler = slog.LevelInfo
New(leveler, cfg.LogAddSource, cfg.LogLevelKey, cfg.LogLevelOverrides, cfg.LogTimeFormat, cfg.LogTimeKey, true)
}