bitbucket.org/ai69/amoy@v0.2.3/logger.go (about) 1 package amoy 2 3 import ( 4 "os" 5 "strings" 6 7 "github.com/1set/gut/yos" 8 "go.uber.org/zap" 9 "go.uber.org/zap/zapcore" 10 lj "gopkg.in/natefinch/lumberjack.v2" 11 ) 12 13 // Logger is a wrapper of uber/zap logger with dynamic log level. 14 type Logger struct { 15 logL *zap.Logger 16 logS *zap.SugaredLogger 17 minLevel *zap.AtomicLevel 18 } 19 20 // LogConfig stands for config of logging. 21 type LogConfig struct { 22 ConsoleFormat string 23 FileFormat string 24 Monochrome bool 25 MaxFileSizeMB int 26 MaxBackups int 27 CompressFile bool 28 } 29 30 var ( 31 getEncoderConfig = func(lvlEnc zapcore.LevelEncoder) *zapcore.EncoderConfig { 32 return &zapcore.EncoderConfig{ 33 TimeKey: "time", 34 LevelKey: "level", 35 NameKey: "logger", 36 CallerKey: "caller", 37 MessageKey: "msg", 38 StacktraceKey: "stacktrace", 39 LineEnding: zapcore.DefaultLineEnding, 40 EncodeLevel: lvlEnc, 41 EncodeTime: zapcore.ISO8601TimeEncoder, 42 EncodeDuration: zapcore.StringDurationEncoder, 43 EncodeCaller: zapcore.ShortCallerEncoder, 44 } 45 } 46 // check if the level is greater than or equal to error 47 allLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 48 return true 49 }) 50 // log encoders 51 consoleColorEncoder = zapcore.NewConsoleEncoder(*getEncoderConfig(zapcore.CapitalColorLevelEncoder)) 52 consoleMonoEncoder = zapcore.NewConsoleEncoder(*getEncoderConfig(zapcore.CapitalLevelEncoder)) 53 jsonEncoder = zapcore.NewJSONEncoder(*getEncoderConfig(zapcore.LowercaseLevelEncoder)) 54 // std log writers 55 writeStdout = zapcore.AddSync(os.Stdout) 56 writeStderr = zapcore.AddSync(os.Stderr) 57 ) 58 59 // NewLogger returns a Logger with given log path and debug mode. 60 func NewLogger(fileName string, debug bool, cfgs ...LogConfig) *Logger { 61 // log enablers 62 minLevel := zap.NewAtomicLevelAt(zap.DebugLevel) 63 customLevelEnabler := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 64 return lvl >= minLevel.Level() 65 }) 66 normalLevelEnabler := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 67 return lvl < zapcore.ErrorLevel && lvl >= minLevel.Level() 68 }) 69 errorLevelEnabler := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 70 return lvl >= zapcore.ErrorLevel && lvl >= minLevel.Level() 71 }) 72 73 cfg := LogConfig{ 74 ConsoleFormat: "console", 75 FileFormat: "json", 76 Monochrome: false, 77 MaxFileSizeMB: 100, 78 CompressFile: true, 79 } 80 if len(cfgs) > 0 { 81 cfg = cfgs[0] 82 } 83 84 // log level encoder 85 consoleEncoder := consoleColorEncoder 86 if yos.IsOnWindows() || cfg.Monochrome { 87 consoleEncoder = consoleMonoEncoder 88 } 89 90 // combine core for logger 91 var cores []zapcore.Core 92 switch strings.ToLower(cfg.ConsoleFormat) { 93 case "console": 94 cores = []zapcore.Core{ 95 zapcore.NewCore(consoleEncoder, writeStdout, normalLevelEnabler), 96 zapcore.NewCore(consoleEncoder, writeStderr, errorLevelEnabler), 97 } 98 case "json": 99 fallthrough 100 default: 101 cores = []zapcore.Core{ 102 zapcore.NewCore(jsonEncoder, writeStdout, normalLevelEnabler), 103 zapcore.NewCore(jsonEncoder, writeStderr, errorLevelEnabler), 104 } 105 } 106 107 // set log file path as per parameters 108 if logPath := strings.TrimSpace(fileName); len(logPath) > 0 { 109 logFile := zapcore.AddSync(&lj.Logger{ 110 Filename: logPath, 111 MaxSize: cfg.MaxFileSizeMB, 112 Compress: cfg.CompressFile, 113 }) 114 115 switch strings.ToLower(cfg.FileFormat) { 116 case "console": 117 cores = append(cores, zapcore.NewCore(consoleEncoder, logFile, customLevelEnabler)) 118 case "json": 119 fallthrough 120 default: 121 cores = append(cores, zapcore.NewCore(jsonEncoder, logFile, customLevelEnabler)) 122 } 123 } 124 125 // combine option for logger 126 options := []zap.Option{ 127 zap.AddCaller(), 128 zap.AddStacktrace(zap.ErrorLevel), 129 } 130 // set debug mode as per parameters 131 if debug { 132 options = append(options, zap.Development()) 133 } 134 135 // build the logger 136 logL := zap.New(zapcore.NewTee(cores...), options...) 137 return &Logger{ 138 logL: logL, 139 logS: logL.Sugar(), 140 minLevel: &minLevel, 141 } 142 } 143 144 // Logger returns a zap logger inside the wrapper. 145 func (l *Logger) Logger() *zap.Logger { 146 return l.logL 147 } 148 149 // LoggerSugared returns a sugared zap logger inside the wrapper. 150 func (l *Logger) LoggerSugared() *zap.SugaredLogger { 151 return l.logS 152 } 153 154 // SetLogLevel sets the log level of loggers inside the wrapper. 155 func (l *Logger) SetLogLevel(level string) { 156 if err := l.minLevel.UnmarshalText([]byte(level)); err != nil { 157 l.logL.DPanic("fail to set log level", zap.Error(err)) 158 } 159 } 160 161 // GetLogLevel returns the log level of loggers inside the wrapper. 162 func (l *Logger) GetLogLevel() zapcore.Level { 163 return l.minLevel.Level() 164 }