zerologのencoderについて話します

9550 ワード

シーケンス


本文は主にzerologのencoderを研究します

encoder


github.com/rs/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/internal/json/types.go
// AppendLineBreak appends a line break.
func (Encoder) AppendLineBreak(dst []byte) []byte {
    return append(dst, '
') }

AppendLineBreak改行文字の追加

AppendInterface


github.com/rs/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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メソッドは、現在のloggerをパッケージした新しいContextを作成します.loggerのcontext属性はbyte配列である.Contextが提供する様々なタイプのAppendメソッドは最後にbyte配列としてloggerのcontext属性に追加される

newEvent


github.com/rs/[email protected]/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
}

新Eventメソッドは、encoderのAppendObjectDataメソッドを介してloggerのcontextプロパティをeventのbufにコピーします.

≪インスタンス|Instance|emdw≫

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メソッドは、現在のloggerをパッケージした新しいContextを作成します.loggerのcontext属性はbyte配列である.Contextが提供する様々なタイプのAppendメソッドは最後にbyte配列としてloggerのcontext属性に追加される.新Eventメソッドは、encoderのAppendObjectDataメソッドを介してloggerのcontextプロパティをeventのbufにコピーします.

doc

  • zerolog