diff --git a/cfg.go b/cfg.go new file mode 100644 index 0000000..dbc62c1 --- /dev/null +++ b/cfg.go @@ -0,0 +1,19 @@ +package tecaminologger + +type Config struct { + MaxSize int // max. size of file in MB + MaxBackup int // + MaxAge int + Debug bool + TerminalOut bool +} + +func DefaultConfig() *Config { + return &Config{ + Debug: false, + TerminalOut: false, + MaxSize: 1, + MaxBackup: 3, + MaxAge: 28, + } +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..aeae682 --- /dev/null +++ b/logger.go @@ -0,0 +1,107 @@ +package tecaminologger + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" +) + +type Level zapcore.Level + +const ( + Info Level = 0 + Warning Level = 1 + Error Level = 2 + Debug Level = -1 +) + +type Logger struct { + log *zap.Logger + debug *zap.Logger + debugging bool +} + +func NewLogger(file string, cfg *Config) (*Logger, error) { + + if cfg == nil { + cfg = DefaultConfig() + } + + if file == "" { + file = os.Args[0] + if filepath.Ext(file) != "" { + file = strings.Replace(filepath.Base(file), filepath.Ext(file), ".log", 1) + } else { + file = filepath.Base(file) + ".log" + } + } + + encoderConfig := zap.NewProductionEncoderConfig() + encoderConfig.TimeKey = "timestamp" + encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006-01-02T15:04:05.000")) + } + encoder := zapcore.NewJSONEncoder(encoderConfig) + + fileSyncer := zapcore.AddSync(&lumberjack.Logger{ + Filename: file, + MaxSize: cfg.MaxSize, // megabytes + MaxBackups: cfg.MaxBackup, + MaxAge: cfg.MaxAge, // days + }) + + // Create core list + var cores []zapcore.Core + + // File core (always added) + cores = append(cores, zapcore.NewCore(encoder, fileSyncer, zapcore.InfoLevel)) + + // Optional stdout core + if cfg.TerminalOut { + stdoutSyncer := zapcore.AddSync(zapcore.Lock(os.Stdout)) + cores = append(cores, zapcore.NewCore(encoder, stdoutSyncer, zapcore.InfoLevel)) + } + + combinedCore := zapcore.NewTee(cores...) + + // If debugging, add a separate debug core (to both file and stdout if requested) + var debugCores []zapcore.Core + debugCores = append(debugCores, zapcore.NewCore(encoder, fileSyncer, zapcore.DebugLevel)) + if cfg.TerminalOut { + stdoutSyncer := zapcore.AddSync(zapcore.Lock(os.Stdout)) + debugCores = append(debugCores, zapcore.NewCore(encoder, stdoutSyncer, zapcore.DebugLevel)) + } + debugCore := zapcore.NewTee(debugCores...) + + // Build the logger + return &Logger{ + log: zap.New(combinedCore), + debug: zap.New(debugCore), + debugging: cfg.Debug, + }, nil +} + +func (l *Logger) Info(caller, msg string) { + l.log.Info(msg, zap.String("caller", caller)) +} + +func (l *Logger) Warning(caller, msg string) { + l.log.Warn(msg, zap.String("caller", caller)) +} + +func (l *Logger) Error(caller string, msg any) { + l.log.Error(fmt.Sprint(msg), zap.String("caller", caller)) +} + +func (l *Logger) Debug(caller, msg string) { + if !l.debugging { + return + } + l.debug.Debug(msg, zap.String("caller", caller)) +}