美文网首页
聊聊golang的zap的Field

聊聊golang的zap的Field

作者: go4it | 来源:发表于2020-12-18 23:21 被阅读0次

    本文主要研究一下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

    相关文章

      网友评论

          本文标题:聊聊golang的zap的Field

          本文链接:https://www.haomeiwen.com/subject/ldqqnktx.html