聊聊golang的zap的CheckedEntry

899 阅读2分钟

本文主要研究一下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