本文次要钻研一下golang的zap的Field

Field

zap@v1.16.0/zapcore/field.go

type Field struct {    Key       string    Type      FieldType    Integer   int64    String    string    Interface interface{}}
Field定义了Key、FieldType、Integer、String、Interface属性

AddTo

zap@v1.16.0/zapcore/field.go

func (f Field) AddTo(enc ObjectEncoder) {    var err error    switch f.Type {    case ArrayMarshalerType:        err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))    case ObjectMarshalerType:        err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))    case BinaryType:        enc.AddBinary(f.Key, f.Interface.([]byte))    case BoolType:        enc.AddBool(f.Key, f.Integer == 1)    case ByteStringType:        enc.AddByteString(f.Key, f.Interface.([]byte))    case Complex128Type:        enc.AddComplex128(f.Key, f.Interface.(complex128))    case Complex64Type:        enc.AddComplex64(f.Key, f.Interface.(complex64))    case DurationType:        enc.AddDuration(f.Key, time.Duration(f.Integer))    case Float64Type:        enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))    case Float32Type:        enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))    case Int64Type:        enc.AddInt64(f.Key, f.Integer)    case Int32Type:        enc.AddInt32(f.Key, int32(f.Integer))    case Int16Type:        enc.AddInt16(f.Key, int16(f.Integer))    case Int8Type:        enc.AddInt8(f.Key, int8(f.Integer))    case StringType:        enc.AddString(f.Key, f.String)    case TimeType:        if f.Interface != nil {            enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))        } else {            // Fall back to UTC if location is nil.            enc.AddTime(f.Key, time.Unix(0, f.Integer))        }    case TimeFullType:        enc.AddTime(f.Key, f.Interface.(time.Time))    case Uint64Type:        enc.AddUint64(f.Key, uint64(f.Integer))    case Uint32Type:        enc.AddUint32(f.Key, uint32(f.Integer))    case Uint16Type:        enc.AddUint16(f.Key, uint16(f.Integer))    case Uint8Type:        enc.AddUint8(f.Key, uint8(f.Integer))    case UintptrType:        enc.AddUintptr(f.Key, uintptr(f.Integer))    case ReflectType:        err = enc.AddReflected(f.Key, f.Interface)    case NamespaceType:        enc.OpenNamespace(f.Key)    case StringerType:        err = encodeStringer(f.Key, f.Interface, enc)    case ErrorType:        encodeError(f.Key, f.Interface.(error), enc)    case SkipType:        break    default:        panic(fmt.Sprintf("unknown field type: %v", f))    }    if err != nil {        enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())    }}
AddTo办法依据Field的类型来执行encoder的对应办法将Field的key和value增加到encoder中

Equals

zap@v1.16.0/zapcore/field.go

func (f Field) Equals(other Field) bool {    if f.Type != other.Type {        return false    }    if f.Key != other.Key {        return false    }    switch f.Type {    case BinaryType, ByteStringType:        return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte))    case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType:        return reflect.DeepEqual(f.Interface, other.Interface)    default:        return f == other    }}
Equals办法用于判断两个Field是否相等,对于BinaryType或ByteStringType应用bytes.Equal判断,对于ArrayMarshalerType、ObjectMarshalerType、ErrorType、ReflectType应用reflect.DeepEqual判断,其余的默认用==判断

addFields

zap@v1.16.0/zapcore/field.go

func addFields(enc ObjectEncoder, fields []Field) {    for i := range fields {        fields[i].AddTo(enc)    }}
addFields办法用于将fields批量增加到encoder中

With

zap@v1.16.0/zapcore/core.go

func (c *ioCore) With(fields []Field) Core {    clone := c.clone()    addFields(clone.enc, fields)    return clone}
zapcore的With办法用于将fields增加到core中,core的Field属于全局通用的Field

logger

zap@v1.16.0/logger.go

func (log *Logger) With(fields ...Field) *Logger {    if len(fields) == 0 {        return log    }    l := log.clone()    l.core = l.core.With(fields)    return l}func (log *Logger) Info(msg string, fields ...Field) {    if ce := log.check(InfoLevel, msg); ce != nil {        ce.Write(fields...)    }}
logger的With办法最初是执行core的With,增加的是全局的;而Info之类办法提供的Field参数属于动静的,每条log本人的动静Field,它最初调用的是encoder的EncodeEntry(Entry, []Field)办法

Write

zap@v1.16.0/zapcore/json_encoder.go

func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {    final := enc.clone()    final.buf.AppendByte('{')    if final.LevelKey != "" {        final.addKey(final.LevelKey)        cur := final.buf.Len()        final.EncodeLevel(ent.Level, final)        if cur == final.buf.Len() {            // User-supplied EncodeLevel was a no-op. Fall back to strings to keep            // output JSON valid.            final.AppendString(ent.Level.String())        }    }    if final.TimeKey != "" {        final.AddTime(final.TimeKey, ent.Time)    }    if ent.LoggerName != "" && final.NameKey != "" {        final.addKey(final.NameKey)        cur := final.buf.Len()        nameEncoder := final.EncodeName        // if no name encoder provided, fall back to FullNameEncoder for backwards        // compatibility        if nameEncoder == nil {            nameEncoder = FullNameEncoder        }        nameEncoder(ent.LoggerName, final)        if cur == final.buf.Len() {            // User-supplied EncodeName was a no-op. Fall back to strings to            // keep output JSON valid.            final.AppendString(ent.LoggerName)        }    }    if ent.Caller.Defined {        if final.CallerKey != "" {            final.addKey(final.CallerKey)            cur := final.buf.Len()            final.EncodeCaller(ent.Caller, final)            if cur == final.buf.Len() {                // User-supplied EncodeCaller was a no-op. Fall back to strings to                // keep output JSON valid.                final.AppendString(ent.Caller.String())            }        }        if final.FunctionKey != "" {            final.addKey(final.FunctionKey)            final.AppendString(ent.Caller.Function)        }    }    if final.MessageKey != "" {        final.addKey(enc.MessageKey)        final.AppendString(ent.Message)    }    if enc.buf.Len() > 0 {        final.addElementSeparator()        final.buf.Write(enc.buf.Bytes())    }    addFields(final, fields)    final.closeOpenNamespaces()    if ent.Stack != "" && final.StacktraceKey != "" {        final.AddString(final.StacktraceKey, ent.Stack)    }    final.buf.AppendByte('}')    if final.LineEnding != "" {        final.buf.AppendString(final.LineEnding)    } else {        final.buf.AppendString(DefaultLineEnding)    }    ret := final.buf    putJSONEncoder(final)    return ret, nil}
jsonEncoder的Write办法执行的是Field的addFields(final, fields),将Field的key、value增加到encoder中

实例

func fieldDemo() {    logger, err := zap.NewProduction()    defer logger.Sync()    if err != nil {        panic(err)    }    logger = logger.With(zap.String("appId", "demoApp"))    logger.Info("failed to fetch URL",        // Structured context as strongly typed Field values.        zap.String("url", "https://example.com"),        zap.Int("attempt", 3),        zap.Duration("backoff", time.Second),    )}

输入

{"level":"info","ts":1608304623.277035,"caller":"zap/zap_demo.go:28","msg":"failed to fetch URL","appId":"demoApp","url":"https://example.com","attempt":3,"backoff":1}

小结

Field的AddTo办法依据Field的类型来执行encoder的对应办法将Field的key和value增加到encoder中;logger的With办法最初是执行core的With,增加的是全局的;而Info之类办法提供的Field参数属于动静的,每条log本人的动静Field,它最初调用的是encoder的EncodeEntry(Entry, []Field)办法

doc

  • zap