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  }