本文主要研究一下zerolog的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方法
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
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
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用于追加换行符
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
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数组
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方法
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属性中
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中。