序
本文次要钻研一下 golang 的 zap 的 CheckedEntry
Entry
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 属性
CheckedEntry
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 属性
reset
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 属性
Write
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 来执行对应的操作
AddCore
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,相当于多增加一个输入
Should
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
_cePool
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 时重置其属性。
doc
- zap