对于应用的调试,我们经常会使用 fmt.Println来输出关键变量的数据。或者使用 log 库,将数据以 log 的形式输出。对于基础数据类型,上面两种方法都可以比较方便地满足需求。对于一些结构体类型数据,通常我们可以先将其序列化后再输出。

如果结构体中包含不可序列化的字段,比如 func 类型,那么序列化就会抛出错误,阻碍调试。

go-spew

上面的需求,go-spew 可以完美地帮我们实现。go-spew 可以以一种非常友好的方式输出完整的数据结构信息。如:

  1. s := "GoCN"
  2. i := 123
  3. spew.Dump(s, i)
  4. //-----
  5. (string) (len=4) "GoCN"
  6. (int) 123

对于复杂的数据类型:

  1. type S struct {
  2. S2 *S2
  3. I *int
  4. }
  5. type S2 struct {
  6. Str string
  7. }
  8. func main() {
  9. s := "GoCN"
  10. i := 2
  11. f := []float64{1.1, 2.2}
  12. m := map[string]int{"a": 1, "b": 2}
  13. e := errors.New("new error")
  14. ss := S{S2: &S2{Str: "xxx"}, I: &i}
  15. spew.Dump(s, i, f, m, e, ss)
  16. }
  17. //-----
  18. (string) (len=4) "GoCN"
  19. (int) 2
  20. ([]float64) (len=2 cap=2) {
  21. (float64) 1.1,
  22. (float64) 2.2
  23. }
  24. (map[string]int) (len=2) {
  25. (string) (len=1) "a": (int) 1,
  26. (string) (len=1) "b": (int) 2
  27. }
  28. (*errors.errorString)(0xc000010320)(new error)
  29. (main.S) {
  30. S2: (*main.S2)(0xc000010330)({
  31. Str: (string) (len=3) "xxx"
  32. }),
  33. I: (*int)(0xc00001e1f0)(2)
  34. }

可以看到,对于 map、slice、嵌套 struct 等类型的数据都可以友好地展示出来。包括长度、字段类型、字段值、指针值以及指针指向的数据等。

  1. u := &url.URL{Scheme: "https", Host: "gocn.vip"}
  2. req, err := http.NewRequestWithContext(context.Background(), "GET", u.String(), nil)
  3. if err != nil {
  4. panic(err)
  5. }
  6. spew.Dump(req)
  7. //-----
  8. (*http.Request)(0xc000162000)({
  9. Method: (string) (len=3) "GET",
  10. URL: (*url.URL)(0xc000136090)(https://gocn.vip),
  11. Proto: (string) (len=8) "HTTP/1.1",
  12. ProtoMajor: (int) 1,
  13. ProtoMinor: (int) 1,
  14. Header: (http.Header) {
  15. },
  16. Body: (io.ReadCloser) <nil>,
  17. GetBody: (func() (io.ReadCloser, error)) <nil>,
  18. ContentLength: (int64) 0,
  19. TransferEncoding: ([]string) <nil>,
  20. Close: (bool) false,
  21. Host: (string) (len=8) "gocn.vip",
  22. Form: (url.Values) <nil>,
  23. PostForm: (url.Values) <nil>,
  24. MultipartForm: (*multipart.Form)(<nil>),
  25. Trailer: (http.Header) <nil>,
  26. RemoteAddr: (string) "",
  27. RequestURI: (string) "",
  28. TLS: (*tls.ConnectionState)(<nil>),
  29. Cancel: (<-chan struct {}) <nil>,
  30. Response: (*http.Response)(<nil>),
  31. ctx: (*context.emptyCtx)(0xc000020090)(context.Background)
  32. })

上面是一个 http.Request 类型的变量,其中包含多种复杂的数据类型,但 go-spew 都可以友好地输出出来,非常方便我们调试。

Dump系列函数

go-spew有三个 Dump 系列函数:

  • Dump() 标准输出到os.Stdout
  • Fdump() 允许输出自定义io.Writer
  • Sdump() 输出的结果作为字符串返回

此外,还有 Printf、 Fprintf、Sprintf 几个函数来支持定制输出风格。用法与 fmt 的函数相似。

自定义配置

go-spew 支持一些自定义配置,可以通过 spew.Config 修改。

一些常用配置如下:

  • Indent 修改分隔符
  • MaxDepth 控制遍历最大深度
  • DisablePointerAddresses 控制是否打印指针类型数据地址

此外还有其他很多配置,大家可以自己测试一下,这里不再举例。

小结

go-spew 是一个非常完美的输出 Go 数据结构的调试工具,并且经过了全面的测试,测试覆盖率为100%。该工具支持各种自定义配置,非常方便,可以有效提升我们日常开发的效率。