首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >golang源码分析:davecgh/go-spew

golang源码分析:davecgh/go-spew

作者头像
golangLeetcode
发布2023-09-06 19:27:18
发布2023-09-06 19:27:18
4710
举报

spew是一个更友好的变量打印工具,github.com/davecgh/go-spew,它能打印指针指向的内容,避免直接打印的时候打印出来的是指针地址的问题,同时能够检测结构体中存在的环,解决了json序列化打印的时候不能打印环的问题,它提供了很多接口,可以打印到文件、json、和标准输出,逻辑都差不多,我们详细分析下打印到标准输出逻辑的实现。

代码语言:javascript
复制
  func Dump(a ...interface{}) {
  fdump(&Config, os.Stdout, a...)
}

源码位于:github.com/davecgh/go-spew@v1.1.1/spew/dump.go,它会依次打印变长参数列表里面的每一个变量,如果遇到nil变量,会打印出 (interface {}) <nil> <nil>

代码语言:javascript
复制
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
        for _, arg := range a {
    if arg == nil {
          w.Write(interfaceBytes)
      w.Write(spaceBytes)
      w.Write(nilAngleBytes)
      w.Write(newlineBytes)

    d := dumpState{w: w, cs: cs}
    d.pointers = make(map[uintptr]int)
    d.dump(reflect.ValueOf(arg))
    d.w.Write(newlineBytes)
代码语言:javascript
复制
            interfaceBytes        = []byte("(interface {})")
            spaceBytes            = []byte(" ")
            nilAngleBytes         = []byte("<nil>")
            newlineBytes          = []byte("\n")

如果遇到非nil变量,会调用 d.dump函数其中d定义如下

代码语言:javascript
复制
type dumpState struct {
  w                io.Writer
  depth            int
  pointers         map[uintptr]int
  ignoreNextType   bool
  ignoreNextIndent bool
  cs               *ConfigState
}

函数内部根据值的类型进行打印

代码语言:javascript
复制
func (d *dumpState) dump(v reflect.Value) {
  kind := v.Kind()
  if kind == reflect.Invalid {
    d.w.Write(invalidAngleBytes)       
  if kind == reflect.Ptr {
    d.indent()
    d.dumpPtr(v)
  if !d.ignoreNextType {
    d.indent()
    d.w.Write(openParenBytes)
          valueLen, valueCap := 0, 0
  switch v.Kind() {
  case reflect.Array, reflect.Slice, reflect.Chan:
    valueLen, valueCap = v.Len(), v.Cap()
  case reflect.Map, reflect.String:
    valueLen = v.Len()
  }
  
  if valueLen != 0 
      d.w.Write(lenEqualsBytes)
      printInt(d.w, int64(valueLen), 10)


if !d.cs.DisableMethods {
    if (kind != reflect.Invalid) && (kind != reflect.Interface) {
      if handled := handleMethods(d.cs, d.w, v); handled {
      

switch kind {
  case reflect.Invalid:
          case reflect.Array:
    d.w.Write(openBraceNewlineBytes)
          case reflect.Struct:
    d.w.Write(openBraceNewlineBytes)
          case reflect.UnsafePointer, reflect.Chan, reflect.Func:
    printHexPtr(d.w, v.Pointer())

invalidAngleBytes = []byte("<invalid>")

输出指针类型的时候,会根据熟悉所在层级,根据深度填空格

代码语言:javascript
复制
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))

如果是数组,slice或者chan会输出len和cap等信息

代码语言:javascript
复制
lenEqualsBytes        = []byte("len=")
capEqualsBytes        = []byte("cap=")

如果有方法列表需要打印方法列表。其中输出指针类型的详细如下: 在输出的过程中会判断是否有环,如果有环只打印一遍,再次走到,打印

代码语言:javascript
复制
  circularBytes         = []byte("<already shown>")
代码语言:javascript
复制
func (d *dumpState) dumpPtr(v reflect.Value) {
          for k, depth := range d.pointers {
    if depth >= d.depth {
      delete(d.pointers, k)
      
  for ve.Kind() == reflect.Ptr {
    if ve.IsNil() {
      nilFound = true
        pointerChain = append(pointerChain, addr)
    if pd, ok := d.pointers[addr]; ok && pd < d.depth {
          d.w.Write(openParenBytes)
      d.w.Write(bytes.Repeat(asteriskBytes, indirects))
      

打印方法的逻辑位于:github.com/davecgh/go-spew@v1.1.1/spew/common.go会对interface进行特殊处理

代码语言:javascript
复制
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
      if !v.CanInterface() {
    if UnsafeDisabled {
      v = unsafeReflectValue(v)
      switch iface := v.Interface().(type) {
  case error:
    defer catchPanic(w, v)
    if cs.ContinueOnMethod {
        case fmt.Stringer:
    defer catchPanic(w, v)
    if cs.ContinueOnMethod {
      w.Write(openParenBytes)

github.com/davecgh/go-spew@v1.1.1/spew/bypass.go

代码语言:javascript
复制
func unsafeReflectValue(v reflect.Value) reflect.Value {
  flagFieldPtr := flagField(&v)
  *flagFieldPtr &^= flagRO
  *flagFieldPtr |= flagAddr
  return v

本质上是利用反射reflect.Value获取值信息,然后打印出值信息,附加一些类型的属性信息,解决了直接打印指针问题和json序列化环的问题。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-05-16,如有侵权请联系 cloudcommunity@tencent.com 删除
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档