本文次要钻研一下golang的zap的global.go

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

StdLog

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

// 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来撤销这种重定向。

doc

  • zap