序
本文次要钻研一下 golang 的 zap 的 Field
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