本文主要研究一下golang的zap的CheckedEntry
zap@v1.16.0/zapcore/entry.go
type Entry struct { Level Level Time time.Time LoggerName string Message string Caller EntryCaller Stack string }
Entry定义了Level、Time、LoggerName、Message、Caller、Stack属性
zap@v1.16.0/zapcore/entry.go
type CheckedEntry struct { Entry ErrorOutput WriteSyncer dirty bool // best-effort detection of pool misuse should CheckWriteAction cores []Core }
CheckedEntry内嵌了Entry,定义了ErrorOutput、dirty、CheckWriteAction、cores属性
zap@v1.16.0/zapcore/entry.go
func (ce *CheckedEntry) reset() { ce.Entry = Entry{} ce.ErrorOutput = nil ce.dirty = false ce.should = WriteThenNoop for i := range ce.cores { // don't keep references to cores ce.cores[i] = nil } ce.cores = ce.cores[:0] }
reset会重置CheckedEntry的Entry、ErrorOutput、dirty、CheckWriteAction、cores属性
zap@v1.16.0/zapcore/entry.go
func (ce *CheckedEntry) Write(fields ...Field) { if ce == nil { return } if ce.dirty { if ce.ErrorOutput != nil { // Make a best effort to detect unsafe re-use of this CheckedEntry. // If the entry is dirty, log an internal error; because the // CheckedEntry is being used after it was returned to the pool, // the message may be an amalgamation from multiple call sites. fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", time.Now(), ce.Entry) ce.ErrorOutput.Sync() } return } ce.dirty = true var err error for i := range ce.cores { err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields)) } if ce.ErrorOutput != nil { if err != nil { fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", time.Now(), err) ce.ErrorOutput.Sync() } } should, msg := ce.should, ce.Message putCheckedEntry(ce) switch should { case WriteThenPanic: panic(msg) case WriteThenFatal: exit.Exit() case WriteThenGoexit: runtime.Goexit() } }
Write方法先判断CheckedEntry是否是dirty,如果是则表示该entry复用出问题了,就往ErrorOutput输出错误信息,然后返回;不是dirty的话,就先标注为dirty,然后遍历cores挨个执行core的Write方法,并用multierr来记录错误,如果出现err且ErrorOutput不为nil则往ErrorOutput输出err信息;之后执行putCheckedEntry放入_cePool中,最后根据CheckWriteAction来执行对应的操作
zap@v1.16.0/zapcore/entry.go
func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry { if ce == nil { ce = getCheckedEntry() ce.Entry = ent } ce.cores = append(ce.cores, core) return ce }
AddCore往CheckedEntry的cores添加core,相当于多添加一个输出
zap@v1.16.0/zapcore/entry.go
func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry { if ce == nil { ce = getCheckedEntry() ce.Entry = ent } ce.should = should return ce }
Should用于更新CheckedEntry的CheckWriteAction
zap@v1.16.0/zapcore/entry.go
var ( _cePool = sync.Pool{New: func() interface{} { // Pre-allocate some space for cores. return &CheckedEntry{ cores: make([]Core, 4), } }} ) func getCheckedEntry() *CheckedEntry { ce := _cePool.Get().(*CheckedEntry) ce.reset() return ce } func putCheckedEntry(ce *CheckedEntry) { if ce == nil { return } _cePool.Put(ce) }
_cePool为CheckedEntry的Pool,其New函数创建cores长度为4的CheckedEntry;其getCheckedEntry方法从_cePool取出一个CheckedEntry,然后执行reset,再返回;其putCheckedEntry方法将CheckedEntry归还到_cePool中
func checkedEntryDemo() { entry := zapcore.Entry{ Level: zapcore.InfoLevel, Time: time.Now(), LoggerName: "demoLogger", Message: "hello world", Caller: zapcore.NewEntryCaller(100, "/path/to/foo.go", 42, false), Stack: "this is stack", } ce := &zapcore.CheckedEntry{ Entry: entry, ErrorOutput: zapcore.Lock(os.Stderr), } buf := &bytes.Buffer{} core := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), zapcore.AddSync(buf), zap.InfoLevel) ce.AddCore(entry, core) ce.Write() fmt.Print(buf) }
输出
{"level":"info","ts":1608133564.1996229,"logger":"demoLogger","msg":"hello world","stacktrace":"this is stack"}
CheckedEntry内嵌了Entry,定义了ErrorOutput、dirty、CheckWriteAction、cores属性;entry包使用_cePool来获取和归还CheckedEntry;CheckedEntry提供了AddCore方法往CheckedEntry的cores添加core,其Write会遍历cores挨个执行core的Write方法,其reset方法用于在从pool中取出CheckedEntry时重置其属性。