序
本文主要研究一下golang的zap的ReflectType
sweetenFields[email protected]/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 < len(args); { // This is a strongly-typed field. Consume it and move on. if f, ok := args[i].(Field); ok { fields = append(fields, f) i++ continue } // Make sure this element isn't a dangling key. if i == len(args)-1 { s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i])) break } // Consume this value and the next, treating them as a key-value pair. If the // key isn't a string, add this pair to the slice of invalid pairs. key, val := args[i], args[i+1] if keyStr, ok := key.(string); !ok { // Subsequent errors are likely, so allocate once up front. if cap(invalid) == 0 { invalid = make(invalidPairs, 0, len(args)/2) } invalid = append(invalid, invalidPair{i, key, val}) } else { fields = append(fields, Any(keyStr, val)) } i += 2 } // If we encountered any invalid key-value pairs, log an error. if len(invalid) > 0 { s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid)) } return fields}
sweetenFields方法執行的是fields = append(fields, Any(keyStr, val))
Any[email protected]/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[email protected]/field.go
func Reflect(key string, val interface{}) Field { return Field{Key: key, Type: zapcore.ReflectType, Interface: val}}
Reflect建立的Field型別的Type為zapcore.ReflectType
AddTo[email protected]/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[email protected]/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('\n') 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: "[email protected]", CreatedAt: time.Date(2020, 12, 19, 8, 0, 0, 0, time.UTC), } var users Users users = append(users, &User{ Name: "hello2", Email: "[email protected]", CreatedAt: time.Date(2020, 12, 19, 9, 0, 0, 0, time.UTC), }, &User{ Name: "hello3", Email: "[email protected]", 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":"[email protected]","CreatedAt":"2020-12-19T08:00:00Z"},"users":[{"Name":"hello2","Email":"[email protected]","CreatedAt":"2020-12-19T09:00:00Z"},{"Name":"hello3","Email":"[email protected]","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來序列化。
doczap