共计 4736 个字符,预计需要花费 12 分钟才能阅读完成。
序
本文次要钻研一下 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
正文完