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