聊聊zerolog的encoder

我的梦境 提交于 2021-01-05 00:23:52

本文主要研究一下zerolog的encoder

encoder

github.com/rs/zerolog@v1.20.0/encoder.go

type encoder interface {
	AppendArrayDelim(dst []byte) []byte
	AppendArrayEnd(dst []byte) []byte
	AppendArrayStart(dst []byte) []byte
	AppendBeginMarker(dst []byte) []byte
	AppendBool(dst []byte, val bool) []byte
	AppendBools(dst []byte, vals []bool) []byte
	AppendBytes(dst, s []byte) []byte
	AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool) []byte
	AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool) []byte
	AppendEndMarker(dst []byte) []byte
	AppendFloat32(dst []byte, val float32) []byte
	AppendFloat64(dst []byte, val float64) []byte
	AppendFloats32(dst []byte, vals []float32) []byte
	AppendFloats64(dst []byte, vals []float64) []byte
	AppendHex(dst, s []byte) []byte
	AppendIPAddr(dst []byte, ip net.IP) []byte
	AppendIPPrefix(dst []byte, pfx net.IPNet) []byte
	AppendInt(dst []byte, val int) []byte
	AppendInt16(dst []byte, val int16) []byte
	AppendInt32(dst []byte, val int32) []byte
	AppendInt64(dst []byte, val int64) []byte
	AppendInt8(dst []byte, val int8) []byte
	AppendInterface(dst []byte, i interface{}) []byte
	AppendInts(dst []byte, vals []int) []byte
	AppendInts16(dst []byte, vals []int16) []byte
	AppendInts32(dst []byte, vals []int32) []byte
	AppendInts64(dst []byte, vals []int64) []byte
	AppendInts8(dst []byte, vals []int8) []byte
	AppendKey(dst []byte, key string) []byte
	AppendLineBreak(dst []byte) []byte
	AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte
	AppendNil(dst []byte) []byte
	AppendObjectData(dst []byte, o []byte) []byte
	AppendString(dst []byte, s string) []byte
	AppendStrings(dst []byte, vals []string) []byte
	AppendTime(dst []byte, t time.Time, format string) []byte
	AppendTimes(dst []byte, vals []time.Time, format string) []byte
	AppendUint(dst []byte, val uint) []byte
	AppendUint16(dst []byte, val uint16) []byte
	AppendUint32(dst []byte, val uint32) []byte
	AppendUint64(dst []byte, val uint64) []byte
	AppendUint8(dst []byte, val uint8) []byte
	AppendUints(dst []byte, vals []uint) []byte
	AppendUints16(dst []byte, vals []uint16) []byte
	AppendUints32(dst []byte, vals []uint32) []byte
	AppendUints64(dst []byte, vals []uint64) []byte
	AppendUints8(dst []byte, vals []uint8) []byte
}

encoder接口定义了一系列的Append方法

AppendMarker

github.com/rs/zerolog@v1.20.0/internal/json/types.go

// AppendBeginMarker inserts a map start into the dst byte array.
func (Encoder) AppendBeginMarker(dst []byte) []byte {
	return append(dst, '{')
}

// AppendEndMarker inserts a map end into the dst byte array.
func (Encoder) AppendEndMarker(dst []byte) []byte {
	return append(dst, '}')
}

AppendBeginMarker及AppendEndMarker用于追加map start和end

AppendArray

github.com/rs/zerolog@v1.20.0/internal/json/types.go

// AppendArrayStart adds markers to indicate the start of an array.
func (Encoder) AppendArrayStart(dst []byte) []byte {
	return append(dst, '[')
}

// AppendArrayEnd adds markers to indicate the end of an array.
func (Encoder) AppendArrayEnd(dst []byte) []byte {
	return append(dst, ']')
}

AppendArrayStart及AppendArrayEnd用于追加array start和end

AppendLineBreak

github.com/rs/zerolog@v1.20.0/internal/json/types.go

// AppendLineBreak appends a line break.
func (Encoder) AppendLineBreak(dst []byte) []byte {
	return append(dst, '\n')
}

AppendLineBreak用于追加换行符

AppendInterface

github.com/rs/zerolog@v1.20.0/internal/json/types.go

// AppendInterface marshals the input interface to a string and
// appends the encoded string to the input byte slice.
func (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {
	marshaled, err := json.Marshal(i)
	if err != nil {
		return e.AppendString(dst, fmt.Sprintf("marshaling error: %v", err))
	}
	return append(dst, marshaled...)
}

AppendInterface方法使用json来序列化interface

AppendObjectData

github.com/rs/zerolog@v1.20.0/internal/json/types.go

// AppendObjectData takes in an object that is already in a byte array
// and adds it to the dst.
func (Encoder) AppendObjectData(dst []byte, o []byte) []byte {
	// Three conditions apply here:
	// 1. new content starts with '{' - which should be dropped   OR
	// 2. new content starts with '{' - which should be replaced with ','
	//    to separate with existing content OR
	// 3. existing content has already other fields
	if o[0] == '{' {
		if len(dst) > 1 {
			dst = append(dst, ',')
		}
		o = o[1:]
	} else if len(dst) > 1 {
		dst = append(dst, ',')
	}
	return append(dst, o...)
}

AppendObjectData用于追加byte数组

Context

github.com/rs/zerolog@v1.20.0/context.go

// Context configures a new sub-logger with contextual fields.
type Context struct {
	l Logger
}

// Logger returns the logger with the context previously set.
func (c Context) Logger() Logger {
	return c.l
}

func (c Context) Array(key string, arr LogArrayMarshaler) Context {
	c.l.context = enc.AppendKey(c.l.context, key)
	if arr, ok := arr.(*Array); ok {
		c.l.context = arr.write(c.l.context)
		return c
	}
	var a *Array
	if aa, ok := arr.(*Array); ok {
		a = aa
	} else {
		a = Arr()
		arr.MarshalZerologArray(a)
	}
	c.l.context = a.write(c.l.context)
	return c
}

// Object marshals an object that implement the LogObjectMarshaler interface.
func (c Context) Object(key string, obj LogObjectMarshaler) Context {
	e := newEvent(levelWriterAdapter{ioutil.Discard}, 0)
	e.Object(key, obj)
	c.l.context = enc.AppendObjectData(c.l.context, e.buf)
	putEvent(e)
	return c
}

// EmbedObject marshals and Embeds an object that implement the LogObjectMarshaler interface.
func (c Context) EmbedObject(obj LogObjectMarshaler) Context {
	e := newEvent(levelWriterAdapter{ioutil.Discard}, 0)
	e.EmbedObject(obj)
	c.l.context = enc.AppendObjectData(c.l.context, e.buf)
	putEvent(e)
	return c
}

func (c Context) RawJSON(key string, b []byte) Context {
	c.l.context = appendJSON(enc.AppendKey(c.l.context, key), b)
	return c
}

// Interface adds the field key with obj marshaled using reflection.
func (c Context) Interface(key string, i interface{}) Context {
	c.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), i)
	return c
}

Context提供了各种类型的方法,其里头执行的是encoder的对应类型的Append方法

With

github.com/rs/zerolog@v1.20.0/log.go

type Logger struct {
	w       LevelWriter
	level   Level
	sampler Sampler
	context []byte
	hooks   []Hook
}

// With creates a child logger with the field added to its context.
func (l Logger) With() Context {
	context := l.context
	l.context = make([]byte, 0, 500)
	if context != nil {
		l.context = append(l.context, context...)
	} else {
		// This is needed for AppendKey to not check len of input
		// thus making it inlinable
		l.context = enc.AppendBeginMarker(l.context)
	}
	return Context{l}
}

With方法创建一个新的Context,包装了当前的logger;logger的context属性为byte数组;Context提供的各种类型的Append方法最后都作为byte数组追加到logger的context属性中

newEvent

github.com/rs/zerolog@v1.20.0/log.go

func (l *Logger) newEvent(level Level, done func(string)) *Event {
	enabled := l.should(level)
	if !enabled {
		return nil
	}
	e := newEvent(l.w, level)
	e.done = done
	e.ch = l.hooks
	if level != NoLevel {
		e.Str(LevelFieldName, LevelFieldMarshalFunc(level))
	}
	if l.context != nil && len(l.context) > 1 {
		e.buf = enc.AppendObjectData(e.buf, l.context)
	}
	return e
}

newEvent方法会将logger的context属性通过encoder的AppendObjectData方法拷贝到event的buf中

实例

func withDemo() {
	logger := zerolog.New(os.Stderr).With().Timestamp().Str("key", "value").Logger()
	logger.Info().Str("k1", "v1").Msg("hello world")
	logger.Info().Str("k2", "v2").Msg("hello world")
}

输出

{"level":"info","key":"value","k1":"v1","time":"2021-01-04T23:45:10+08:00","message":"hello world"}
{"level":"info","key":"value","k2":"v2","time":"2021-01-04T23:45:10+08:00","message":"hello world"}

小结

encoder接口定义了一系列的Append方法;Context提供了各种类型的方法,其里头执行的是encoder的对应类型的Append方法;With方法创建一个新的Context,包装了当前的logger;logger的context属性为byte数组;Context提供的各种类型的Append方法最后都作为byte数组追加到logger的context属性中;newEvent方法会将logger的context属性通过encoder的AppendObjectData方法拷贝到event的buf中。

doc

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!