13

聊聊golang的zap的global.go

 3 years ago
source link: https://studygolang.com/articles/32133
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

聊聊golang的zap的global.go

codecraft · 27天之前 · 127 次点击 · 预计阅读时间 6 分钟 · 不到1分钟之前 开始浏览    

本文主要研究一下golang的zap的global.go

global.go

[email protected]/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

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


有疑问加站长微信联系(非本文作者)

280

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:1006366459


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK