github.com/kotovmak/go-admin@v1.1.1/modules/logger/logger.go (about) 1 // Copyright 2019 GoAdmin Core Team. All rights reserved. 2 // Use of this source code is governed by a Apache-2.0 style 3 // license that can be found in the LICENSE file. 4 5 package logger 6 7 import ( 8 "os" 9 "path/filepath" 10 "strconv" 11 12 "github.com/kotovmak/go-admin/context" 13 "github.com/kotovmak/go-admin/modules/utils" 14 "github.com/mgutz/ansi" 15 "github.com/natefinch/lumberjack" 16 "go.uber.org/zap" 17 "go.uber.org/zap/zapcore" 18 ) 19 20 var ( 21 defaultEncoderCfg = EncoderCfg{ 22 TimeKey: "ts", 23 LevelKey: "level", 24 NameKey: "logger", 25 CallerKey: "caller", 26 MessageKey: "msg", 27 StacktraceKey: "stacktrace", 28 Level: "capitalColor", 29 Time: "ISO8601", 30 Duration: "seconds", 31 Caller: "short", 32 Encoding: "console", 33 } 34 defaultRotateCfg = RotateCfg{ 35 MaxSize: 10, 36 MaxBackups: 5, 37 MaxAge: 30, 38 Compress: false, 39 } 40 41 logger = &Logger{ 42 rotate: defaultRotateCfg, 43 encoder: defaultEncoderCfg, 44 Level: zapcore.InfoLevel, 45 } 46 47 infoLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 48 return lvl == zapcore.InfoLevel 49 }) 50 51 errorLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 52 return lvl >= zapcore.ErrorLevel 53 }) 54 55 accessLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 56 return lvl == zapcore.WarnLevel 57 }) 58 ) 59 60 func init() { 61 logger.Init() 62 } 63 64 type Logger struct { 65 logger *zap.Logger 66 sugaredLogger *zap.SugaredLogger 67 68 infoLogOff bool 69 errorLogOff bool 70 accessLogOff bool 71 72 accessAssetsLogOff bool 73 74 debug bool 75 76 sqlLogOpen bool 77 78 infoLogPath string 79 errorLogPath string 80 accessLogPath string 81 82 rotate RotateCfg 83 encoder EncoderCfg 84 85 Level zapcore.Level 86 } 87 88 type EncoderCfg struct { 89 TimeKey string 90 LevelKey string 91 NameKey string 92 CallerKey string 93 MessageKey string 94 StacktraceKey string 95 Level string 96 Time string 97 Duration string 98 Caller string 99 Encoding string 100 } 101 102 type RotateCfg struct { 103 MaxSize int 104 MaxBackups int 105 MaxAge int 106 Compress bool 107 } 108 109 func (l *Logger) Init() { 110 zapLogger := zap.New(zapcore.NewTee( 111 zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.infoLogPath), infoLevelEnabler), 112 zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.errorLogPath), errorLevelEnabler), 113 zapcore.NewCore(l.getEncoder(""), l.getLogWriter(l.accessLogPath), accessLevelEnabler), 114 ), zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(errorLevelEnabler)) 115 l.sugaredLogger = zapLogger.Sugar() 116 l.logger = zapLogger 117 } 118 119 func (l *Logger) getEncoder(levelKey string) zapcore.Encoder { 120 121 var ( 122 timeEncoder = new(zapcore.TimeEncoder) 123 durationEncoder = new(zapcore.DurationEncoder) 124 callerEncoder = new(zapcore.CallerEncoder) 125 nameEncoder = new(zapcore.NameEncoder) 126 levelEncoder = new(zapcore.LevelEncoder) 127 ) 128 129 _ = timeEncoder.UnmarshalText([]byte(l.encoder.Time)) 130 _ = durationEncoder.UnmarshalText([]byte(l.encoder.Duration)) 131 _ = callerEncoder.UnmarshalText([]byte(l.encoder.Caller)) 132 _ = nameEncoder.UnmarshalText([]byte("full")) 133 _ = levelEncoder.UnmarshalText([]byte(l.encoder.Level)) 134 135 encoderConfig := zapcore.EncoderConfig{ 136 TimeKey: l.encoder.TimeKey, 137 LevelKey: levelKey, 138 NameKey: l.encoder.NameKey, 139 CallerKey: l.encoder.CallerKey, 140 MessageKey: l.encoder.MessageKey, 141 StacktraceKey: l.encoder.StacktraceKey, 142 LineEnding: zapcore.DefaultLineEnding, 143 EncodeLevel: *levelEncoder, 144 EncodeTime: *timeEncoder, 145 EncodeDuration: *durationEncoder, 146 EncodeCaller: *callerEncoder, 147 EncodeName: *nameEncoder, 148 } 149 150 return filterZapEncoder(l.encoder.Encoding, encoderConfig) 151 } 152 153 func (l *Logger) getLogWriter(path string) zapcore.WriteSyncer { 154 if path != "" { 155 lumberJackLogger := &lumberjack.Logger{ 156 Filename: path, 157 MaxSize: l.rotate.MaxSize, 158 MaxBackups: l.rotate.MaxBackups, 159 MaxAge: l.rotate.MaxAge, 160 Compress: l.rotate.Compress, 161 } 162 if l.debug { 163 return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger)) 164 } 165 return zapcore.AddSync(lumberJackLogger) 166 } 167 return zapcore.AddSync(os.Stdout) 168 } 169 170 func (l *Logger) SetRotate(cfg RotateCfg) { 171 if cfg.MaxSize != 0 && cfg.MaxAge != 0 && cfg.MaxBackups != 0 { 172 l.rotate = cfg 173 } 174 } 175 176 func (l *Logger) SetEncoder(cfg EncoderCfg) { 177 cfg.TimeKey = utils.SetDefault(cfg.TimeKey, "", defaultEncoderCfg.TimeKey) 178 cfg.LevelKey = utils.SetDefault(cfg.LevelKey, "", defaultEncoderCfg.LevelKey) 179 cfg.NameKey = utils.SetDefault(cfg.NameKey, "", defaultEncoderCfg.NameKey) 180 cfg.CallerKey = utils.SetDefault(cfg.CallerKey, "", defaultEncoderCfg.CallerKey) 181 cfg.MessageKey = utils.SetDefault(cfg.MessageKey, "", defaultEncoderCfg.MessageKey) 182 cfg.StacktraceKey = utils.SetDefault(cfg.StacktraceKey, "", defaultEncoderCfg.StacktraceKey) 183 cfg.Level = utils.SetDefault(cfg.Level, "", defaultEncoderCfg.Level) 184 cfg.Time = utils.SetDefault(cfg.Time, "", defaultEncoderCfg.Time) 185 cfg.Duration = utils.SetDefault(cfg.Duration, "", defaultEncoderCfg.Duration) 186 cfg.Caller = utils.SetDefault(cfg.Caller, "", defaultEncoderCfg.Caller) 187 cfg.Encoding = utils.SetDefault(cfg.Encoding, "", defaultEncoderCfg.Encoding) 188 l.encoder = cfg 189 } 190 191 type Config struct { 192 InfoLogOff bool 193 ErrorLogOff bool 194 AccessLogOff bool 195 196 SqlLogOpen bool 197 198 InfoLogPath string 199 ErrorLogPath string 200 AccessLogPath string 201 202 AccessAssetsLogOff bool 203 204 Rotate RotateCfg 205 Encode EncoderCfg 206 207 Level int8 208 209 Debug bool 210 } 211 212 func InitWithConfig(cfg Config) { 213 logger.infoLogPath = cfg.InfoLogPath 214 logger.infoLogOff = cfg.InfoLogOff 215 logger.errorLogPath = cfg.ErrorLogPath 216 logger.errorLogOff = cfg.ErrorLogOff 217 logger.accessLogPath = cfg.AccessLogPath 218 logger.accessLogOff = cfg.AccessLogOff 219 logger.sqlLogOpen = cfg.SqlLogOpen 220 logger.accessAssetsLogOff = cfg.AccessAssetsLogOff 221 logger.debug = cfg.Debug 222 logger.SetRotate(cfg.Rotate) 223 logger.SetEncoder(cfg.Encode) 224 logger.Level = filterZapAtomicLevelByViper(cfg.Level) 225 logger.Init() 226 } 227 228 func SetRotate(cfg RotateCfg) { 229 logger.rotate = cfg 230 logger.Init() 231 } 232 233 // OpenSQLLog set the sqlLogOpen true. 234 func OpenSQLLog() { 235 logger.sqlLogOpen = true 236 } 237 238 // Debug print the debug message. 239 func Debug(info ...interface{}) { 240 if !logger.infoLogOff { 241 if logger.Level <= zapcore.DebugLevel { 242 logger.sugaredLogger.Info(info...) 243 } 244 } 245 } 246 247 // Debugf print the debug message. 248 func Debugf(template string, args ...interface{}) { 249 if !logger.infoLogOff && logger.Level <= zapcore.DebugLevel { 250 logger.sugaredLogger.Infof(template, args...) 251 } 252 } 253 254 // Info print the info message. 255 func Info(info ...interface{}) { 256 if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { 257 logger.sugaredLogger.Info(info...) 258 } 259 } 260 261 // Info print the info message. 262 func Infof(template string, args ...interface{}) { 263 if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { 264 logger.sugaredLogger.Infof(template, args...) 265 } 266 } 267 268 // Warn print the warning message. 269 func Warn(info ...interface{}) { 270 if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { 271 logger.sugaredLogger.Warn(info...) 272 } 273 } 274 275 // Warnf print the warning message. 276 func Warnf(template string, args ...interface{}) { 277 if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { 278 logger.sugaredLogger.Warnf(template, args...) 279 } 280 } 281 282 // Error print the error message. 283 func Error(err ...interface{}) { 284 if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { 285 logger.sugaredLogger.Error(err...) 286 } 287 } 288 289 // Errorf print the error message. 290 func Errorf(template string, args ...interface{}) { 291 if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { 292 logger.sugaredLogger.Errorf(template, args...) 293 } 294 } 295 296 // Fatal print the fatal message. 297 func Fatal(info ...interface{}) { 298 if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { 299 logger.sugaredLogger.Fatal(info...) 300 } 301 } 302 303 // Fatalf print the fatal message. 304 func Fatalf(template string, args ...interface{}) { 305 if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { 306 logger.sugaredLogger.Fatalf(template, args...) 307 } 308 } 309 310 // Fatal print the panic message. 311 func Panic(info ...interface{}) { 312 logger.sugaredLogger.Panic(info...) 313 } 314 315 // Panicf print the panic message. 316 func Panicf(template string, args ...interface{}) { 317 logger.sugaredLogger.Panicf(template, args...) 318 } 319 320 // Access print the access message. 321 func Access(ctx *context.Context) { 322 if !logger.accessLogOff && logger.Level <= zapcore.InfoLevel { 323 temp := "[GoAdmin] %s %s %s" 324 if logger.accessAssetsLogOff { 325 if filepath.Ext(ctx.Path()) == "" { 326 logger.sugaredLogger.Warnf(temp, 327 ansi.Color(" "+strconv.Itoa(ctx.Response.StatusCode)+" ", "white:blue"), 328 ansi.Color(" "+string(ctx.Method())+" ", "white:blue+h"), 329 ctx.Path()) 330 } 331 } else { 332 logger.sugaredLogger.Warnf(temp, 333 ansi.Color(" "+strconv.Itoa(ctx.Response.StatusCode)+" ", "white:blue"), 334 ansi.Color(" "+string(ctx.Method())+" ", "white:blue+h"), 335 ctx.Path()) 336 } 337 } 338 } 339 340 // LogSQL print the sql info message. 341 func LogSQL(statement string, args []interface{}) { 342 if !logger.infoLogOff && logger.sqlLogOpen && statement != "" { 343 if logger.Level <= zapcore.InfoLevel { 344 logger.sugaredLogger.With("statement", statement, "args", args).Info("[GoAdmin]") 345 } 346 } 347 } 348 349 func filterZapEncoder(encoding string, encoderConfig zapcore.EncoderConfig) zapcore.Encoder { 350 var encoder zapcore.Encoder 351 switch encoding { 352 default: 353 encoder = zapcore.NewConsoleEncoder(encoderConfig) 354 case "json": 355 encoder = zapcore.NewJSONEncoder(encoderConfig) 356 case "console": 357 encoder = zapcore.NewConsoleEncoder(encoderConfig) 358 } 359 return encoder 360 } 361 362 func filterZapAtomicLevelByViper(level int8) zapcore.Level { 363 var atomViper zapcore.Level 364 switch level { 365 default: 366 atomViper = zap.InfoLevel 367 case -1: 368 atomViper = zap.DebugLevel 369 case 0: 370 atomViper = zap.InfoLevel 371 case 1: 372 atomViper = zap.WarnLevel 373 case 2: 374 atomViper = zap.ErrorLevel 375 } 376 return atomViper 377 }