本文主要研究一下golang的zap的global.go
zap@v1.16.0/global.go
var ( _globalMu sync.RWMutex _globalL = NewNop() _globalS = _globalL.Sugar() ) // L returns the global Logger, which can be reconfigured with ReplaceGlobals. // It's safe for concurrent use. func L() *Logger { _globalMu.RLock() l := _globalL _globalMu.RUnlock() return l } // S returns the global SugaredLogger, which can be reconfigured with // ReplaceGlobals. It's safe for concurrent use. func S() *SugaredLogger { _globalMu.RLock() s := _globalS _globalMu.RUnlock() return s } // ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a // function to restore the original values. It's safe for concurrent use. func ReplaceGlobals(logger *Logger) func() { _globalMu.Lock() prev := _globalL _globalL = logger _globalS = logger.Sugar() _globalMu.Unlock() return func() { ReplaceGlobals(prev) } }
global定义了_globalL及_globalS,L()方法通过加读锁的方式返回_globalL,S()通过加读锁的方式返回_globalS,之所以要加读锁是因为ReplaceGlobals方法会修改_globalL及_globalS
zap@v1.16.0/global.go
// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at // InfoLevel. To redirect the standard library's package-global logging // functions, use RedirectStdLog instead. func NewStdLog(l *Logger) *log.Logger { logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) f := logger.Info return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */) } // NewStdLogAt returns *log.Logger which writes to supplied zap logger at // required level. func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) { logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) logFunc, err := levelToFunc(logger, level) if err != nil { return nil, err } return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil } func levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) { switch lvl { case DebugLevel: return logger.Debug, nil case InfoLevel: return logger.Info, nil case WarnLevel: return logger.Warn, nil case ErrorLevel: return logger.Error, nil case DPanicLevel: return logger.DPanic, nil case PanicLevel: return logger.Panic, nil case FatalLevel: return logger.Fatal, nil } return nil, fmt.Errorf("unrecognized level: %q", lvl) } type loggerWriter struct { logFunc func(msg string, fields ...Field) } func (l *loggerWriter) Write(p []byte) (int, error) { p = bytes.TrimSpace(p) l.logFunc(string(p)) return len(p), nil }
NewStdLog返回使用info级别的log.Logger;NewStdLogAt方法返回使用指定级别的log.Logger;levelToFunc用于返回指定level对应的zap.Logger的func;这里使用loggerWriter来包装zap.Logger到标准库的log.Logger
// RedirectStdLog redirects output from the standard library's package-global // logger to the supplied logger at InfoLevel. Since zap already handles caller // annotations, timestamps, etc., it automatically disables the standard // library's annotations and prefixing. // // It returns a function to restore the original prefix and flags and reset the // standard library's output to os.Stderr. func RedirectStdLog(l *Logger) func() { f, err := redirectStdLogAt(l, InfoLevel) if err != nil { // Can't get here, since passing InfoLevel to redirectStdLogAt always // works. panic(fmt.Sprintf(_programmerErrorTemplate, err)) } return f } // RedirectStdLogAt redirects output from the standard library's package-global // logger to the supplied logger at the specified level. Since zap already // handles caller annotations, timestamps, etc., it automatically disables the // standard library's annotations and prefixing. // // It returns a function to restore the original prefix and flags and reset the // standard library's output to os.Stderr. func RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { return redirectStdLogAt(l, level) } func redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) { flags := log.Flags() prefix := log.Prefix() log.SetFlags(0) log.SetPrefix("") logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) logFunc, err := levelToFunc(logger, level) if err != nil { return nil, err } log.SetOutput(&loggerWriter{logFunc}) return func() { log.SetFlags(flags) log.SetPrefix(prefix) log.SetOutput(os.Stderr) }, nil }
RedirectStdLog把标准库的log.Logger的info级别的输出重定向到zap.Logger;RedirectStdLogAt把标准库的log.Logger的指定级别的输出重定向到zap.Logger
func globalDemo() { logger, _ := zap.NewProduction() defer logger.Sync() // flushes buffer, if any zap.ReplaceGlobals(logger) zap.S().Info("hello") stdLog := zap.NewStdLog(logger) stdLog.Println("this is standard logger but log with output to zap logger") undo := zap.RedirectStdLog(logger) log.Println("standard log will redirect to zap.Logger") undo() log.Println("standard log with original output") }
输出
{"level":"info","ts":1607860586.171433,"caller":"zap/zap_demo.go:23","msg":"hello"} {"level":"info","ts":1607860586.171505,"caller":"zap/zap_demo.go:26","msg":"this is standard logger but log with output to zap logger"} {"level":"info","ts":1607860586.1715178,"caller":"zap/zap_demo.go:29","msg":"standard log will redirect to zap.Logger"} 2020/12/13 19:56:26 standard log with original output
global.go提供了ReplaceGlobals方法用于注册全局的单例的logger;提供了NewStdLog方法用于返回标准库的log.Logger,然后使用该logger的输出都会通过zap.Logger来输出;提供了RedirectStdLog方法用于改变全局的标准库的log的输出,将其通过zap.Logger来输出,该方法返回一个func来撤销这种重定向。