乐趣区

关于golang:聊聊golang的zap的Field

本文次要钻研一下 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
退出移动版