github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/shared/mlog/log.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package mlog 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "log" 11 "os" 12 "sync" 13 "sync/atomic" 14 "time" 15 16 "github.com/mattermost/logr" 17 "go.uber.org/zap" 18 "go.uber.org/zap/zapcore" 19 "gopkg.in/natefinch/lumberjack.v2" 20 ) 21 22 const ( 23 // Very verbose messages for debugging specific issues 24 LevelDebug = "debug" 25 // Default log level, informational 26 LevelInfo = "info" 27 // Warnings are messages about possible issues 28 LevelWarn = "warn" 29 // Errors are messages about things we know are problems 30 LevelError = "error" 31 32 // DefaultFlushTimeout is the default amount of time mlog.Flush will wait 33 // before timing out. 34 DefaultFlushTimeout = time.Second * 5 35 ) 36 37 var ( 38 // disableZap is set when Zap should be disabled and Logr used instead. 39 // This is needed for unit testing as Zap has no shutdown capabilities 40 // and holds file handles until process exit. Currently unit test create 41 // many server instances, and thus many Zap log files. 42 // This flag will be removed when Zap is permanently replaced. 43 disableZap int32 44 ) 45 46 // Type and function aliases from zap to limit the libraries scope into MM code 47 type Field = zapcore.Field 48 49 var Int64 = zap.Int64 50 var Int32 = zap.Int32 51 var Int = zap.Int 52 var Uint32 = zap.Uint32 53 var String = zap.String 54 var Any = zap.Any 55 var Err = zap.Error 56 var NamedErr = zap.NamedError 57 var Bool = zap.Bool 58 var Duration = zap.Duration 59 60 type LoggerIFace interface { 61 IsLevelEnabled(LogLevel) bool 62 Debug(string, ...Field) 63 Info(string, ...Field) 64 Warn(string, ...Field) 65 Error(string, ...Field) 66 Critical(string, ...Field) 67 Log(LogLevel, string, ...Field) 68 LogM([]LogLevel, string, ...Field) 69 } 70 71 type TargetInfo logr.TargetInfo 72 73 type LoggerConfiguration struct { 74 EnableConsole bool 75 ConsoleJson bool 76 EnableColor bool 77 ConsoleLevel string 78 EnableFile bool 79 FileJson bool 80 FileLevel string 81 FileLocation string 82 } 83 84 type Logger struct { 85 zap *zap.Logger 86 consoleLevel zap.AtomicLevel 87 fileLevel zap.AtomicLevel 88 logrLogger *logr.Logger 89 mutex *sync.RWMutex 90 } 91 92 func getZapLevel(level string) zapcore.Level { 93 switch level { 94 case LevelInfo: 95 return zapcore.InfoLevel 96 case LevelWarn: 97 return zapcore.WarnLevel 98 case LevelDebug: 99 return zapcore.DebugLevel 100 case LevelError: 101 return zapcore.ErrorLevel 102 default: 103 return zapcore.InfoLevel 104 } 105 } 106 107 func makeEncoder(json, color bool) zapcore.Encoder { 108 encoderConfig := zap.NewProductionEncoderConfig() 109 if json { 110 return zapcore.NewJSONEncoder(encoderConfig) 111 } 112 113 if color { 114 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder 115 } 116 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 117 return zapcore.NewConsoleEncoder(encoderConfig) 118 } 119 120 func NewLogger(config *LoggerConfiguration) *Logger { 121 cores := []zapcore.Core{} 122 logger := &Logger{ 123 consoleLevel: zap.NewAtomicLevelAt(getZapLevel(config.ConsoleLevel)), 124 fileLevel: zap.NewAtomicLevelAt(getZapLevel(config.FileLevel)), 125 logrLogger: newLogr(), 126 mutex: &sync.RWMutex{}, 127 } 128 129 if config.EnableConsole { 130 writer := zapcore.Lock(os.Stderr) 131 core := zapcore.NewCore(makeEncoder(config.ConsoleJson, config.EnableColor), writer, logger.consoleLevel) 132 cores = append(cores, core) 133 } 134 135 if config.EnableFile { 136 if atomic.LoadInt32(&disableZap) != 0 { 137 t := &LogTarget{ 138 Type: "file", 139 Format: "json", 140 Levels: mlogLevelToLogrLevels(config.FileLevel), 141 MaxQueueSize: DefaultMaxTargetQueue, 142 Options: []byte(fmt.Sprintf(`{"Filename":"%s", "MaxSizeMB":%d, "Compress":%t}`, 143 config.FileLocation, 100, true)), 144 } 145 if !config.FileJson { 146 t.Format = "plain" 147 } 148 if tgt, err := NewLogrTarget("mlogFile", t); err == nil { 149 logger.logrLogger.Logr().AddTarget(tgt) 150 } else { 151 Error("error creating mlogFile", Err(err)) 152 } 153 } else { 154 writer := zapcore.AddSync(&lumberjack.Logger{ 155 Filename: config.FileLocation, 156 MaxSize: 100, 157 Compress: true, 158 }) 159 160 core := zapcore.NewCore(makeEncoder(config.FileJson, false), writer, logger.fileLevel) 161 cores = append(cores, core) 162 } 163 } 164 165 combinedCore := zapcore.NewTee(cores...) 166 167 logger.zap = zap.New(combinedCore, 168 zap.AddCaller(), 169 ) 170 return logger 171 } 172 173 func (l *Logger) ChangeLevels(config *LoggerConfiguration) { 174 l.consoleLevel.SetLevel(getZapLevel(config.ConsoleLevel)) 175 l.fileLevel.SetLevel(getZapLevel(config.FileLevel)) 176 } 177 178 func (l *Logger) SetConsoleLevel(level string) { 179 l.consoleLevel.SetLevel(getZapLevel(level)) 180 } 181 182 func (l *Logger) With(fields ...Field) *Logger { 183 newLogger := *l 184 newLogger.zap = newLogger.zap.With(fields...) 185 if newLogger.getLogger() != nil { 186 ll := newLogger.getLogger().WithFields(zapToLogr(fields)) 187 newLogger.logrLogger = &ll 188 } 189 return &newLogger 190 } 191 192 func (l *Logger) StdLog(fields ...Field) *log.Logger { 193 return zap.NewStdLog(l.With(fields...).zap.WithOptions(getStdLogOption())) 194 } 195 196 // StdLogAt returns *log.Logger which writes to supplied zap logger at required level. 197 func (l *Logger) StdLogAt(level string, fields ...Field) (*log.Logger, error) { 198 return zap.NewStdLogAt(l.With(fields...).zap.WithOptions(getStdLogOption()), getZapLevel(level)) 199 } 200 201 // StdLogWriter returns a writer that can be hooked up to the output of a golang standard logger 202 // anything written will be interpreted as log entries accordingly 203 func (l *Logger) StdLogWriter() io.Writer { 204 newLogger := *l 205 newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(4), getStdLogOption()) 206 f := newLogger.Info 207 return &loggerWriter{f} 208 } 209 210 func (l *Logger) WithCallerSkip(skip int) *Logger { 211 newLogger := *l 212 newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(skip)) 213 return &newLogger 214 } 215 216 // Made for the plugin interface, wraps mlog in a simpler interface 217 // at the cost of performance 218 func (l *Logger) Sugar() *SugarLogger { 219 return &SugarLogger{ 220 wrappedLogger: l, 221 zapSugar: l.zap.Sugar(), 222 } 223 } 224 225 func (l *Logger) IsLevelEnabled(level LogLevel) bool { 226 return isLevelEnabled(l.getLogger(), logr.Level(level)) 227 } 228 229 func (l *Logger) Debug(message string, fields ...Field) { 230 l.zap.Debug(message, fields...) 231 if isLevelEnabled(l.getLogger(), logr.Debug) { 232 l.getLogger().WithFields(zapToLogr(fields)).Debug(message) 233 } 234 } 235 236 func (l *Logger) Info(message string, fields ...Field) { 237 l.zap.Info(message, fields...) 238 if isLevelEnabled(l.getLogger(), logr.Info) { 239 l.getLogger().WithFields(zapToLogr(fields)).Info(message) 240 } 241 } 242 243 func (l *Logger) Warn(message string, fields ...Field) { 244 l.zap.Warn(message, fields...) 245 if isLevelEnabled(l.getLogger(), logr.Warn) { 246 l.getLogger().WithFields(zapToLogr(fields)).Warn(message) 247 } 248 } 249 250 func (l *Logger) Error(message string, fields ...Field) { 251 l.zap.Error(message, fields...) 252 if isLevelEnabled(l.getLogger(), logr.Error) { 253 l.getLogger().WithFields(zapToLogr(fields)).Error(message) 254 } 255 } 256 257 func (l *Logger) Critical(message string, fields ...Field) { 258 l.zap.Error(message, fields...) 259 if isLevelEnabled(l.getLogger(), logr.Error) { 260 l.getLogger().WithFields(zapToLogr(fields)).Error(message) 261 } 262 } 263 264 func (l *Logger) Log(level LogLevel, message string, fields ...Field) { 265 l.getLogger().WithFields(zapToLogr(fields)).Log(logr.Level(level), message) 266 } 267 268 func (l *Logger) LogM(levels []LogLevel, message string, fields ...Field) { 269 var logger *logr.Logger 270 for _, lvl := range levels { 271 if isLevelEnabled(l.getLogger(), logr.Level(lvl)) { 272 // don't create logger with fields unless at least one level is active. 273 if logger == nil { 274 l := l.getLogger().WithFields(zapToLogr(fields)) 275 logger = &l 276 } 277 logger.Log(logr.Level(lvl), message) 278 } 279 } 280 } 281 282 func (l *Logger) Flush(cxt context.Context) error { 283 return l.getLogger().Logr().FlushWithTimeout(cxt) 284 } 285 286 // ShutdownAdvancedLogging stops the logger from accepting new log records and tries to 287 // flush queues within the context timeout. Once complete all targets are shutdown 288 // and any resources released. 289 func (l *Logger) ShutdownAdvancedLogging(cxt context.Context) error { 290 err := l.getLogger().Logr().ShutdownWithTimeout(cxt) 291 l.setLogger(newLogr()) 292 return err 293 } 294 295 // ConfigAdvancedLoggingConfig (re)configures advanced logging based on the 296 // specified log targets. This is the easiest way to get the advanced logger 297 // configured via a config source such as file. 298 func (l *Logger) ConfigAdvancedLogging(targets LogTargetCfg) error { 299 if err := l.ShutdownAdvancedLogging(context.Background()); err != nil { 300 Error("error shutting down previous logger", Err(err)) 301 } 302 303 err := logrAddTargets(l.getLogger(), targets) 304 return err 305 } 306 307 // AddTarget adds one or more logr.Target to the advanced logger. This is the preferred method 308 // to add custom targets or provide configuration that cannot be expressed via a 309 // config source. 310 func (l *Logger) AddTarget(targets ...logr.Target) error { 311 return l.getLogger().Logr().AddTarget(targets...) 312 } 313 314 // RemoveTargets selectively removes targets that were previously added to this logger instance 315 // using the passed in filter function. The filter function should return true to remove the target 316 // and false to keep it. 317 func (l *Logger) RemoveTargets(ctx context.Context, f func(ti TargetInfo) bool) error { 318 // Use locally defined TargetInfo type so we don't spread Logr dependencies. 319 fc := func(tic logr.TargetInfo) bool { 320 return f(TargetInfo(tic)) 321 } 322 return l.getLogger().Logr().RemoveTargets(ctx, fc) 323 } 324 325 // EnableMetrics enables metrics collection by supplying a MetricsCollector. 326 // The MetricsCollector provides counters and gauges that are updated by log targets. 327 func (l *Logger) EnableMetrics(collector logr.MetricsCollector) error { 328 return l.getLogger().Logr().SetMetricsCollector(collector) 329 } 330 331 // getLogger is a concurrent safe getter of the logr logger 332 func (l *Logger) getLogger() *logr.Logger { 333 defer l.mutex.RUnlock() 334 l.mutex.RLock() 335 return l.logrLogger 336 } 337 338 // setLogger is a concurrent safe setter of the logr logger 339 func (l *Logger) setLogger(logger *logr.Logger) { 340 defer l.mutex.Unlock() 341 l.mutex.Lock() 342 l.logrLogger = logger 343 } 344 345 // DisableZap is called to disable Zap, and Logr will be used instead. Any Logger 346 // instances created after this call will only use Logr. 347 // 348 // This is needed for unit testing as Zap has no shutdown capabilities 349 // and holds file handles until process exit. Currently unit tests create 350 // many server instances, and thus many Zap log file handles. 351 // 352 // This method will be removed when Zap is permanently replaced. 353 func DisableZap() { 354 atomic.StoreInt32(&disableZap, 1) 355 } 356 357 // EnableZap re-enables Zap such that any Logger instances created after this 358 // call will allow Zap targets. 359 func EnableZap() { 360 atomic.StoreInt32(&disableZap, 0) 361 }