您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

聊聊golang的zap的ReflectType

时间:2020-12-22 10:22:33  来源:  作者:

本文主要研究一下golang的zap的ReflectType

聊聊golang的zap的ReflectType

 

sweetenFields

zap@v1.16.0/sugar.go

func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
    if len(args) == 0 {
        return nil
    }

    // Allocate enough space for the worst case; if users pass only structured
    // fields, we shouldn't penalize them with extra allocations.
    fields := make([]Field, 0, len(args))
    var invalid invalidPairs

    for i := 0; i < len(args); {
        // This is a strongly-typed field. Consume it and move on.
        if f, ok := args[i].(Field); ok {
            fields = Append(fields, f)
            i++
            continue
        }

        // Make sure this element isn't a dangling key.
        if i == len(args)-1 {
            s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i]))
            break
        }

        // Consume this value and the next, treating them as a key-value pair. If the
        // key isn't a string, add this pair to the slice of invalid pairs.
        key, val := args[i], args[i+1]
        if keyStr, ok := key.(string); !ok {
            // Subsequent errors are likely, so allocate once up front.
            if cap(invalid) == 0 {
                invalid = make(invalidPairs, 0, len(args)/2)
            }
            invalid = append(invalid, invalidPair{i, key, val})
        } else {
            fields = append(fields, Any(keyStr, val))
        }
        i += 2
    }

    // If we encountered any invalid key-value pairs, log an error.
    if len(invalid) > 0 {
        s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid))
    }
    return fields
}

sweetenFields方法执行的是fields = append(fields, Any(keyStr, val))

Any

zap@v1.16.0/field.go

func Any(key string, value interface{}) Field {
    switch val := value.(type) {
    case zapcore.ObjectMarshaler:
        return Object(key, val)
    case zapcore.ArrayMarshaler:
        return Array(key, val)
    case bool:
        return Bool(key, val)
    case *bool:
        return Boolp(key, val)
    case []bool:
        return Bools(key, val)
    case complex128:
        return Complex128(key, val)
    case *complex128:
        return Complex128p(key, val)
    case []complex128:
        return Complex128s(key, val)
    case complex64:
        return Complex64(key, val)
    case *complex64:
        return Complex64p(key, val)
    case []complex64:
        return Complex64s(key, val)
    case float64:
        return Float64(key, val)
    case *float64:
        return Float64p(key, val)
    case []float64:
        return Float64s(key, val)
    case float32:
        return Float32(key, val)
    case *float32:
        return Float32p(key, val)
    case []float32:
        return Float32s(key, val)
    case int:
        return Int(key, val)
    case *int:
        return Intp(key, val)
    case []int:
        return Ints(key, val)
    case int64:
        return Int64(key, val)
    case *int64:
        return Int64p(key, val)
    case []int64:
        return Int64s(key, val)
    case int32:
        return Int32(key, val)
    case *int32:
        return Int32p(key, val)
    case []int32:
        return Int32s(key, val)
    case int16:
        return Int16(key, val)
    case *int16:
        return Int16p(key, val)
    case []int16:
        return Int16s(key, val)
    case int8:
        return Int8(key, val)
    case *int8:
        return Int8p(key, val)
    case []int8:
        return Int8s(key, val)
    case string:
        return String(key, val)
    case *string:
        return Stringp(key, val)
    case []string:
        return Strings(key, val)
    case uint:
        return Uint(key, val)
    case *uint:
        return Uintp(key, val)
    case []uint:
        return Uints(key, val)
    case uint64:
        return Uint64(key, val)
    case *uint64:
        return Uint64p(key, val)
    case []uint64:
        return Uint64s(key, val)
    case uint32:
        return Uint32(key, val)
    case *uint32:
        return Uint32p(key, val)
    case []uint32:
        return Uint32s(key, val)
    case uint16:
        return Uint16(key, val)
    case *uint16:
        return Uint16p(key, val)
    case []uint16:
        return Uint16s(key, val)
    case uint8:
        return Uint8(key, val)
    case *uint8:
        return Uint8p(key, val)
    case []byte:
        return Binary(key, val)
    case uintptr:
        return Uintptr(key, val)
    case *uintptr:
        return Uintptrp(key, val)
    case []uintptr:
        return Uintptrs(key, val)
    case time.Time:
        return Time(key, val)
    case *time.Time:
        return Timep(key, val)
    case []time.Time:
        return Times(key, val)
    case time.Duration:
        return Duration(key, val)
    case *time.Duration:
        return Durationp(key, val)
    case []time.Duration:
        return Durations(key, val)
    case error:
        return NamedError(key, val)
    case []error:
        return Errors(key, val)
    case fmt.Stringer:
        return Stringer(key, val)
    default:
        return Reflect(key, val)
    }
}

Any方法会根据value的类型返回不同的Field,如果value没有实现zapcore.ObjectMarshaler、zapcore.ArrayMarshaler,也不是基础类型,则走的是默认的Reflect(key, val)

Reflect

zap@v1.16.0/field.go

func Reflect(key string, val interface{}) Field {
    return Field{Key: key, Type: zapcore.ReflectType, Interface: val}
}

Reflect创建的Field类型的Type为zapcore.ReflectType

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的类型做不同处理,如果是ReflectType类型,则执行的是enc.AddReflected(f.Key, f.Interface)

AddReflected

zap@v1.16.0/zapcore/json_encoder.go

func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error {
    valueBytes, err := enc.encodeReflected(obj)
    if err != nil {
        return err
    }
    enc.addKey(key)
    _, err = enc.buf.Write(valueBytes)
    return err
}

func (enc *jsonEncoder) encodeReflected(obj interface{}) ([]byte, error) {
    if obj == nil {
        return nullLiteralBytes, nil
    }
    enc.resetReflectBuf()
    if err := enc.reflectEnc.Encode(obj); err != nil {
        return nil, err
    }
    enc.reflectBuf.TrimNewline()
    return enc.reflectBuf.Bytes(), nil
}

func (enc *jsonEncoder) resetReflectBuf() {
    if enc.reflectBuf == nil {
        enc.reflectBuf = bufferpool.Get()
        enc.reflectEnc = json.NewEncoder(enc.reflectBuf)

        // For consistency with our custom JSON encoder.
        enc.reflectEnc.SetEscapehtml(false)
    } else {
        enc.reflectBuf.Reset()
    }
}

jsonEncoder的AddReflected方法用enc.encodeReflected(obj)来序列化value;encodeReflected方法执行的是enc.resetReflectBuf()及enc.reflectEnc.Encode(obj);resetReflectBuf方法在reflectBuf为nil时创建reflectBuf及json.NewEncoder(enc.reflectBuf),不为nil时执行reflectBuf.Reset();enc.reflectEnc用的是golang内置的json encoder

json.Encode

/usr/local/go/src/encoding/json/stream.go

func NewEncoder(w io.Writer) *Encoder {
    return &Encoder{w: w, escapeHTML: true}
}

func (enc *Encoder) Encode(v interface{}) error {
    if enc.err != nil {
        return enc.err
    }
    e := newEncodeState()
    err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML})
    if err != nil {
        return err
    }

    // Terminate each value with a newline.
    // This makes the output look a little nicer
    // when debugging, and some kind of space
    // is required if the encoded value was a number,
    // so that the reader knows there aren't more
    // digits coming.
    e.WriteByte('n')

    b := e.Bytes()
    if enc.indentPrefix != "" || enc.indentValue != "" {
        if enc.indentBuf == nil {
            enc.indentBuf = new(bytes.Buffer)
        }
        enc.indentBuf.Reset()
        err = Indent(enc.indentBuf, b, enc.indentPrefix, enc.indentValue)
        if err != nil {
            return err
        }
        b = enc.indentBuf.Bytes()
    }
    if _, err = enc.w.Write(b); err != nil {
        enc.err = err
    }
    encodeStatePool.Put(e)
    return err
}

Encode方法通过encodeState的marshal方法进行序列化,这里它读取了enc.escapeHTML选项

实例

type User struct {
    Name      string
    Email     string
    CreatedAt time.Time
}

type Users []*User

func reflectTypeDemo() {
    logger, err := zap.NewProduction()
    defer logger.Sync()
    if err != nil {
        panic(err)
    }
    var user = &User{
        Name:      "hello1",
        Email:     "hello1@test.com",
        CreatedAt: time.Date(2020, 12, 19, 8, 0, 0, 0, time.UTC),
    }
    var users Users
    users = append(users, &User{
        Name:      "hello2",
        Email:     "hello2@test.com",
        CreatedAt: time.Date(2020, 12, 19, 9, 0, 0, 0, time.UTC),
    }, &User{
        Name:      "hello3",
        Email:     "hello3@test.com",
        CreatedAt: time.Date(2020, 12, 20, 10, 0, 0, 0, time.UTC),
    })
    logger.Sugar().Infow("hello", "user", user, "users", users)
}

输出

{"level":"info","ts":1608350874.177944,"caller":"zap/zap_demo.go:42","msg":"hello","user":{"Name":"hello1","Email":"hello1@test.com","CreatedAt":"2020-12-19T08:00:00Z"},"users":[{"Name":"hello2","Email":"hello2@test.com","CreatedAt":"2020-12-19T09:00:00Z"},{"Name":"hello3","Email":"hello3@test.com","CreatedAt":"2020-12-20T10:00:00Z"}]}

小结

zap的sugar提供Infow方法,它通过sweetenFields方法来将key,value封装为Field;sweetenFields方法使用的是Any方法,它会根据value的类型返回不同的Field,如果value没有实现zapcore.ObjectMarshaler、zapcore.ArrayMarshaler,也不是基础类型,则走的是默认的Reflect(key, val);AddTo方法根据Field的类型做不同处理,如果是ReflectType类型,则执行的是enc.AddReflected(f.Key, f.Interface);jsonEncoder的AddReflected方法使用golang内置的json.Encoder来序列化。

doc

  • zap


Tags:golang   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
golang context 很好用,就使用php实现了github地址 : https://github.com/qq1060656096/php-go-context context使用闭坑指南1. 将一个Context参数作为第一个参数传递给传入和...【详细内容】
2021-11-05  Tags: golang  点击:(41)  评论:(0)  加入收藏
简介工作中经常有定时执行某些代码块的需求,如果是PHP代码,一般写个脚本,然后用Cron实现。Go里提供了两种定时器:Timer(到达指定时间触发且只触发一次)和 Ticker(间隔特定时间触发)...【详细内容】
2021-05-10  Tags: golang  点击:(310)  评论:(0)  加入收藏
不管你学没学过golang,都不妨碍这个21世纪的c语言正变得越来越流行,越来越多的平台服务使用golang来构建,我们熟知的docker就是采用golang语言进行开发设计的。谷歌作为golang...【详细内容】
2021-05-10  Tags: golang  点击:(391)  评论:(0)  加入收藏
之前用 go 写一个小工具的时候, 用到了多个协程之间的通信, 当时随手查了查, 结果查出来一大坨, 简单记录一下. golang中多个协程之间是如何进行通信及数据同步的嘞.共享变...【详细内容】
2021-02-25  Tags: golang  点击:(423)  评论:(0)  加入收藏
12月初,我们发现了一种新的用Golang编写的蠕虫。该蠕虫延续了 Golang在2020年流行的多平台恶意软件趋势。...【详细内容】
2021-01-05  Tags: golang  点击:(174)  评论:(0)  加入收藏
本文主要研究一下golang的zap的ReflectType sweetenFieldszap@v1.16.0/sugar.gofunc (s *SugaredLogger) sweetenFields(args []interface{}) []Field { if len(args) ==...【详细内容】
2020-12-22  Tags: golang  点击:(64)  评论:(0)  加入收藏
Golang的匿名结构是什么?匿名结构就像普通结构一样,但是它没有名称定义,因此不能在代码的其他地方引用。Go中的结构类似于C等其他语言中的结构。它们是字段的类型化集合,用于将...【详细内容】
2020-12-17  Tags: golang  点击:(139)  评论:(0)  加入收藏
前文《理解 Paxos》只包含伪代码,帮助了理解但又不够爽,既然现在都讲究 Talk is cheap. Show me the code.这次就把文章中的伪代码用 Go 语言实现出来,希望能帮助各位朋友更直...【详细内容】
2020-12-15  Tags: golang  点击:(116)  评论:(0)  加入收藏
今天逛github超市时,发现一个非常不错的数据可视化库go-echarts,特分享给大家。介绍在 Golang 这门语言中,目前数据可视化的第三方库还是特别少,go-echarts的开发就是为了填补这...【详细内容】
2020-12-15  Tags: golang  点击:(88)  评论:(0)  加入收藏
由于Golang的语言设计的原因,不管是不是愿意,每个golang开发者的几乎每一段代码都需要与error做缠斗。下面我就简单分析一下golang中的error相关。...【详细内容】
2020-12-02  Tags: golang  点击:(85)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(12)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(196)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(272)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条