在服务治理中,我们有时需要统计业务方服务的依赖包版本号使用情况,以便进行升级和版本兼容,通过 Go 官方提供的 debug.ReadBuildInfo 可以很方便的实现。

debug.ReadBuildInfo

ReadBuildInfo 是 Go 标准库提供的方法,通过该方法可以读取打包程序的构建信息,里面就包括我们想要的依赖包和版本号信息。

1
2
3
// ReadBuildInfo returns the build information embedded in the running binary. 
// The information is available only in binaries built with module support.
func ReadBuildInfo() (info *BuildInfo, ok bool)

可以看到该方法返回了两个返回值:

  • *BuildInfo 表示构建信息
  • ok 表示是否成功拿到构建信息

需要注意的是,只有在启动 go module 时才能正常获取到 BuildInfo

下面我们看下 debug.BuildInfo 结构体。

debug.BuildInfo

根据 Go 官方文档 说明,BuildInfo 表示从运行的二进制文件读取的构建信息。

1
2
3
4
5
type BuildInfo struct {
	Path string    // main 包路径,比如:command-line-arguments
	Main Module    // main 包信息
	Deps []*Module // 依赖包列表信息
}

继续看下 Module 结构体:

1
2
3
4
5
6
type Module struct {
	Path    string  // 包路径
	Version string  // 包版本号
	Sum     string  // 包校验值
	Replace *Module // 表示是否进行了 replace 替换
}

Module 表示一个模块信息,main 包也是个 Module。

下面我们写个代码示例看下 BuildInfo 具体长什么样。

首先,写一个 go.mod:

1
2
3
4
5
6
7
module gomod01

go 1.16

require (
	github.com/davecgh/go-spew v1.1.1
)

然后,编写 main.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import (
	"runtime/debug"

	"github.com/davecgh/go-spew/spew"
)

func main() {
	res, ok := debug.ReadBuildInfo()
	if ok {
		spew.Dump(res)
	}
}

运行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
(*debug.BuildInfo)(0xc0000522a0)({
 Path: (string) (len=22) "command-line-arguments",
 Main: (debug.Module) {
  Path: (string) (len=7) "gomod01",
  Version: (string) (len=7) "(devel)",
  Sum: (string) "",
  Replace: (*debug.Module)(<nil>)
 },
 Deps: ([]*debug.Module) (len=1 cap=1) {
  (*debug.Module)(0xc00006a040)({
   Path: (string) (len=26) "github.com/davecgh/go-spew",
   Version: (string) (len=6) "v1.1.1",
   Sum: (string) (len=47) "h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=",
   Replace: (*debug.Module)(<nil>)
  })
 }
})

可以看到,当前 Go 程序的模块名是 gomod01,以及依赖的第三方包和版本号都能拿到。

小结

  • 官方提供的 debug.ReadBuildInfo 可以很方便的读取依赖包
  • Go 服务必须启用 go module(go1.16 默认开启)