序
本文主要研究一下golang的zap的ReflectType

sweetenFields
zap@v1.16.0/sugar.go
func (s *SugaredLogger) sweetenFields(args []interface{}) []Field { if len(args) == 0 { return nil } // Allocate enough space for the worst case; if users pass only structured // fields, we shouldn't penalize them with extra allocations. fields := make([]Field, 0, len(args)) var invalid invalidPairs for i := 0; i 0 { s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid)) } return fields}
sweetenFields方法执行的是fields = append(fields, Any(keyStr, val))
Any
zap@v1.16.0/field.go
func Any(key string, value interface{}) Field { switch val := value.(type) { case zapcore.ObjectMarshaler: return Object(key, val) case zapcore.ArrayMarshaler: return Array(key, val) case bool: return Bool(key, val) case *bool: return Boolp(key, val) case []bool: return Bools(key, val) case complex128: return Complex128(key, val) case *complex128: return Complex128p(key, val) case []complex128: return Complex128s(key, val) case complex64: return Complex64(key, val) case *complex64: return Complex64p(key, val) case []complex64: return Complex64s(key, val) case float64: return Float64(key, val) case *float64: return Float64p(key, val) case []float64: return Float64s(key, val) case float32: return Float32(key, val) case *float32: return Float32p(key, val) case []float32: return Float32s(key, val) case int: return Int(key, val) case *int: return Intp(key, val) case []int: return Ints(key, val) case int64: return Int64(key, val) case *int64: return Int64p(key, val) case []int64: return Int64s(key, val) case int32: return Int32(key, val) case *int32: return Int32p(key, val) case []int32: return Int32s(key, val) case int16: return Int16(key, val) case *int16: return Int16p(key, val) case []int16: return Int16s(key, val) case int8: return Int8(key, val) case *int8: return Int8p(key, val) case []int8: return Int8s(key, val) case string: return String(key, val) case *string: return Stringp(key, val) case []string: return Strings(key, val) case uint: return Uint(key, val) case *uint: return Uintp(key, val) case []uint: return Uints(key, val) case uint64: return Uint64(key, val) case *uint64: return Uint64p(key, val) case []uint64: return Uint64s(key, val) case uint32: return Uint32(key, val) case *uint32: return Uint32p(key, val) case []uint32: return Uint32s(key, val) case uint16: return Uint16(key, val) case *uint16: return Uint16p(key, val) case []uint16: return Uint16s(key, val) case uint8: return Uint8(key, val) case *uint8: return Uint8p(key, val) case []byte: return Binary(key, val) case uintptr: return Uintptr(key, val) case *uintptr: return Uintptrp(key, val) case []uintptr: return Uintptrs(key, val) case time.Time: return Time(key, val) case *time.Time: return Timep(key, val) case []time.Time: return Times(key, val) case time.Duration: return Duration(key, val) case *time.Duration: return Durationp(key, val) case []time.Duration: return Durations(key, val) case error: return NamedError(key, val) case []error: return Errors(key, val) case fmt.Stringer: return Stringer(key, val) default: return Reflect(key, val) }}
Any方法会根据value的类型返回不同的Field,如果value没有实现zapcore.ObjectMarshaler、zapcore.ArrayMarshaler,也不是基础类型,则走的是默认的Reflect(key, val)
Reflect
zap@v1.16.0/field.go
func Reflect(key string, val interface{}) Field { return Field{Key: key, Type: zapcore.ReflectType, Interface: val}}
Reflect创建的Field类型的Type为zapcore.ReflectType
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的类型做不同处理,如果是ReflectType类型,则执行的是enc.AddReflected(f.Key, f.Interface)
AddReflected
zap@v1.16.0/zapcore/json_encoder.go
func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error { valueBytes, err := enc.encodeReflected(obj) if err != nil { return err } enc.addKey(key) _, err = enc.buf.Write(valueBytes) return err}func (enc *jsonEncoder) encodeReflected(obj interface{}) ([]byte, error) { if obj == nil { return nullLiteralBytes, nil } enc.resetReflectBuf() if err := enc.reflectEnc.Encode(obj); err != nil { return nil, err } enc.reflectBuf.TrimNewline() return enc.reflectBuf.Bytes(), nil}func (enc *jsonEncoder) resetReflectBuf() { if enc.reflectBuf == nil { enc.reflectBuf = bufferpool.Get() enc.reflectEnc = json.NewEncoder(enc.reflectBuf) // For consistency with our custom JSON encoder. enc.reflectEnc.SetEscapeHTML(false) } else { enc.reflectBuf.Reset() }}
jsonEncoder的AddReflected方法用enc.encodeReflected(obj)来序列化value;encodeReflected方法执行的是enc.resetReflectBuf()及enc.reflectEnc.Encode(obj);resetReflectBuf方法在reflectBuf为nil时创建reflectBuf及json.NewEncoder(enc.reflectBuf),不为nil时执行reflectBuf.Reset();enc.reflectEnc用的是golang内置的json encoder
json.Encode
/usr/local/go/src/encoding/json/stream.go
func NewEncoder(w io.Writer) *Encoder { return &Encoder{w: w, escapeHTML: true}}func (enc *Encoder) Encode(v interface{}) error { if enc.err != nil { return enc.err } e := newEncodeState() err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML}) if err != nil { return err } // Terminate each value with a newline. // This makes the output look a little nicer // when debugging, and some kind of space // is required if the encoded value was a number, // so that the reader knows there aren't more // digits coming. e.WriteByte('') b := e.Bytes() if enc.indentPrefix != "" || enc.indentValue != "" { if enc.indentBuf == nil { enc.indentBuf = new(bytes.Buffer) } enc.indentBuf.Reset() err = Indent(enc.indentBuf, b, enc.indentPrefix, enc.indentValue) if err != nil { return err } b = enc.indentBuf.Bytes() } if _, err = enc.w.Write(b); err != nil { enc.err = err } encodeStatePool.Put(e) return err}
Encode方法通过encodeState的marshal方法进行序列化,这里它读取了enc.escapeHTML选项
实例
type User struct { Name string Email string CreatedAt time.Time}type Users []*Userfunc reflectTypeDemo() { logger, err := zap.NewProduction() defer logger.Sync() if err != nil { panic(err) } var user = &User{ Name: "hello1", Email: "hello1@test.com", CreatedAt: time.Date(2020, 12, 19, 8, 0, 0, 0, time.UTC), } var users Users users = append(users, &User{ Name: "hello2", Email: "hello2@test.com", CreatedAt: time.Date(2020, 12, 19, 9, 0, 0, 0, time.UTC), }, &User{ Name: "hello3", Email: "hello3@test.com", CreatedAt: time.Date(2020, 12, 20, 10, 0, 0, 0, time.UTC), }) logger.Sugar().Infow("hello", "user", user, "users", users)}
输出
{"level":"info","ts":1608350874.177944,"caller":"zap/zap_demo.go:42","msg":"hello","user":{"Name":"hello1","Email":"hello1@test.com","CreatedAt":"2020-12-19T08:00:00Z"},"users":[{"Name":"hello2","Email":"hello2@test.com","CreatedAt":"2020-12-19T09:00:00Z"},{"Name":"hello3","Email":"hello3@test.com","CreatedAt":"2020-12-20T10:00:00Z"}]}
小结
zap的sugar提供Infow方法,它通过sweetenFields方法来将key,value封装为Field;sweetenFields方法使用的是Any方法,它会根据value的类型返回不同的Field,如果value没有实现zapcore.ObjectMarshaler、zapcore.ArrayMarshaler,也不是基础类型,则走的是默认的Reflect(key, val);AddTo方法根据Field的类型做不同处理,如果是ReflectType类型,则执行的是enc.AddReflected(f.Key, f.Interface);jsonEncoder的AddReflected方法使用golang内置的json.Encoder来序列化。
doc
- zap