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) }