sequence
This paper mainly studies Golang’s Zap Field
Field
[email protected] / zapcore/field. Go
type Field struct {
Key string
Type FieldType
Integer int64
String string
Interface interface{}
}
Copy the code
Field defines Key, FieldType, Integer, String, and Interface attributes
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()) } }Copy the code
The AddTo method adds the key and value of the Field to the encoder by executing the corresponding method for the Field type
Equals
[email protected] / 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 } }Copy the code
The Equals method is used to determine whether two fields are Equal. For BinaryType or ByteStringType, use bytes.Equal. For ArrayMarshalerType, ObjectMarshalerType, ErrorType, and ReflectType, ReflectType is reflect.DeepEqual. For others, == is used by default
addFields
[email protected] / zapcore/field. Go
func addFields(enc ObjectEncoder, fields []Field) {
for i := range fields {
fields[i].AddTo(enc)
}
}
Copy the code
The addFields method is used to addFields in batches to encoder
With
[email protected] / zapcore/core. Go
func (c *ioCore) With(fields []Field) Core {
clone := c.clone()
addFields(clone.enc, fields)
return clone
}
Copy the code
Zapcore’s With method is used to add fields to core, which is a globally generic Field
logger
[email protected] / 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...) }}Copy the code
Logger’s With method finally executes core’s With, adding global; The Field parameters provided by methods like Info are dynamic. Each log has its own dynamic Field. The last call is EncodeEntry(Entry, []Field) of Encoder
Write
[email protected] / 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 }Copy the code
The jsonEncoder’s Write method performs the addFields(final, fields) of Field, adding the key and value of Field to the encoder
The instance
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), ) }Copy the code
The output
{" 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}Copy the code
summary
The AddTo method of Field executes the corresponding method of encoder according to the type of Field and adds the key and value of Field to the encoder; Logger’s With method finally executes core’s With, adding global; The Field parameters provided by methods like Info are dynamic. Each log has its own dynamic Field. The last call is EncodeEntry(Entry, []Field) of Encoder
doc
- zap