package log import ( "fmt" "io" stdlog "log" "strings" ) type Level int const ( LevelDebug Level = iota LevelInfo LevelWarn LevelError ) var levelLabels = map[Level]string{ LevelDebug: "[DEBUG]", LevelInfo: "[INFO]", LevelWarn: "[WARN]", LevelError: "[ERROR]", } type Logger struct { level Level logger *stdlog.Logger } // New returns a Logger that writes to the given output. Messages below the // configured level are silently dropped. func New(level Level, output io.Writer) *Logger { return &Logger{ level: level, logger: stdlog.New(output, "", stdlog.LstdFlags), } } func (l *Logger) Debug(format string, args ...any) { l.log(LevelDebug, format, args...) } func (l *Logger) Info(format string, args ...any) { l.log(LevelInfo, format, args...) } func (l *Logger) Warn(format string, args ...any) { l.log(LevelWarn, format, args...) } func (l *Logger) Error(format string, args ...any) { l.log(LevelError, format, args...) } func (l *Logger) log(level Level, format string, args ...any) { if level < l.level { return } label := levelLabels[level] msg := fmt.Sprintf(format, args...) l.logger.Printf("%s %s", label, msg) } // ParseLevel converts a string to a Level. Input is case-insensitive. // Valid values: "debug", "info", "warn", "error". func ParseLevel(s string) (Level, error) { switch strings.ToLower(strings.TrimSpace(s)) { case "debug": return LevelDebug, nil case "info": return LevelInfo, nil case "warn": return LevelWarn, nil case "error": return LevelError, nil default: return LevelInfo, fmt.Errorf("unrecognized log level %q; valid values: debug, info, warn, error", s) } }