序
本文主要研究一下golang的zap的Field
![1160dbd4ff2b195d08bd17c796805be4.png](https://img-blog.csdnimg.cn/img_convert/1160dbd4ff2b195d08bd17c796805be4.png)
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