package logger import ( "os" "strings" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // Log levels const ( LevelDebug = "DEBUG" LevelInfo = "INFO" LevelWarn = "WARN" LevelError = "ERROR" ) // Logger represents a leveled zap-backed logger while preserving the package API. type Logger struct { level zap.AtomicLevel sugar *zap.SugaredLogger } var ( // DefaultLogger is the default logger instance. DefaultLogger = NewLogger(LevelInfo) ) func parseLevel(level string) zapcore.Level { switch strings.ToUpper(strings.TrimSpace(level)) { case LevelDebug: return zap.DebugLevel case LevelWarn: return zap.WarnLevel case LevelError: return zap.ErrorLevel default: return zap.InfoLevel } } func newZapLogger(level zap.AtomicLevel) *zap.SugaredLogger { cfg := zap.NewProductionConfig() cfg.Encoding = "console" cfg.Level = level cfg.OutputPaths = []string{"stderr"} cfg.ErrorOutputPaths = []string{"stderr"} cfg.DisableStacktrace = true cfg.EncoderConfig.TimeKey = "ts" cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder cfg.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder cfg.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder cfg.EncoderConfig.CallerKey = "" logger, err := cfg.Build() if err != nil { fallback := zap.New( zapcore.NewCore( zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig()), zapcore.Lock(os.Stderr), level, ), ) return fallback.Sugar() } return logger.Sugar() } // NewLogger creates a new logger instance with the specified log level. func NewLogger(level string) *Logger { atomicLevel := zap.NewAtomicLevelAt(parseLevel(level)) return &Logger{ level: atomicLevel, sugar: newZapLogger(atomicLevel), } } // SetLevel changes the logger level at runtime. func (l *Logger) SetLevel(level string) { if l == nil { return } l.level.SetLevel(parseLevel(level)) } func (l *Logger) logWithFormat(logFn func(string, ...interface{}), plainFn func(...interface{}), message string, args ...interface{}) { if len(args) == 0 { plainFn(message) return } logFn(message, args...) } // Debug logs a debug message. func (l *Logger) Debug(message string, args ...interface{}) { l.logWithFormat(l.sugar.Debugf, l.sugar.Debug, message, args...) } // Info logs an info message. func (l *Logger) Info(message string, args ...interface{}) { l.logWithFormat(l.sugar.Infof, l.sugar.Info, message, args...) } // Warn logs a warning message. func (l *Logger) Warn(message string, args ...interface{}) { l.logWithFormat(l.sugar.Warnf, l.sugar.Warn, message, args...) } // Error logs an error message. func (l *Logger) Error(message string, args ...interface{}) { l.logWithFormat(l.sugar.Errorf, l.sugar.Error, message, args...) } // SetLevel changes the default logger level at runtime. func SetLevel(level string) { if DefaultLogger != nil { DefaultLogger.SetLevel(level) } } // Debug logs a debug message using the default logger. func Debug(message string, args ...interface{}) { DefaultLogger.Debug(message, args...) } // Info logs an info message using the default logger. func Info(message string, args ...interface{}) { DefaultLogger.Info(message, args...) } // Warn logs a warning message using the default logger. func Warn(message string, args ...interface{}) { DefaultLogger.Warn(message, args...) } // Error logs an error message using the default logger. func Error(message string, args ...interface{}) { DefaultLogger.Error(message, args...) }