Golang如何在编译时注入版本信息
Golang如何在编译时注入版本信息
问题
一般而言,稍微做得好一点的开源软件,其二进制文件都会带上版本信息,比如Docker
,你可以通过以下命令来查看:
$ docker --version
Docker version 20.10.17, build 100c701
$ docker version
...
//输出太长,这里就不展示了
有些Web应用还提供了/version
接口,你可以通过接口来获取:
$ curl http://127.0.0.1:8080/version | jq
{
"Build Time": "Fri, 16 Jun 2023 16:19:23 +0800",
"Git Commit": "b554659",
"Go Version": "go1.19",
"OS/Arch": "darwin/amd64",
"version": "0.9.2"
}
然而很多公司并没有这方面的强制性规范,多数开发人员也没有这样的意识或习惯,所以大部分项目并没有实现这个功能。如果二进制文件不带上版本信息,你可能要借助其它系统来查询或追溯,这无疑是一件难受的事,对于代码调试和问题定位很不友好。
那么在Golang
中,如何实现这个功能呢?
思路
版本信息有很多,比如代码版本号、Git提交号、编译时间、Go版本号等,如何让二进制文件带上这些信息呢?
硬编码在代码里肯定不行。因为Git提交号是将代码提交到代码仓库时才产生,而Go版本号和编译时间只有在编译时才能拿到。
比较合理的做法是在编译时注入。Go编译工具提供了-ldflags
选项,通过-X
参数可以注入包变量的值,我们只需要在代码中将版本信息定义成包变量,然后在编译时完成注入即可。这个过程可以实现为自动化完成,下面以make
工具为例说明。
实现步骤
1. 在代码中定义版本信息的变量
将代码版本号、Git提交号、编译时间定义为包变量:
package config
var (
Version string //代码版本号
GitCommit string //Git提交号
BuildTime string //编译时间
)
Go版本号等信息可以借助runtime
包获取:
runtime.Version() //Go版本
runtime.GOOS //操作系统
runtime.GOARCH //平台架构
2. 在代码中使用这些变量
1)实现--version
参数或version
子命令
以Cobra
命令行框架为例,为rootCmd实现--version
参数:
rootCmd.Version = config.Version
rootCmd.SetVersionTemplate(fmt.Sprintf(`{{with .Name}}{{printf "%%s version information: " .}}{{end}}
{{printf "Version: %%s" .Version}}
Git Commit: %s
Build Time: %s
Go version: %s
OS/Arch: %s/%s
`, config.GitCommit, config.BuildTime, runtime.Version(), runtime.GOOS, runtime.GOARCH))
2)实现/version
接口
以Gin
框架为例,将/version
路由到如下的Version
函数:
func Version(ctx *gin.Context) {
ctx.Writer.Header().Set("Content-Type", "application/json")
ctx.Writer.WriteHeader(http.StatusOK)
json.NewEncoder(ctx.Writer).Encode(map[string]string{
"version": config.Version,
"Git Commit": config.GitCommit,
"Build Time": config.BuildTime,
"Go Version": runtime.Version(),
"OS/Arch": runtime.GOOS + "/" + runtime.GOARCH,
})
}
3. 在编译前获取版本信息
在Makefile
中,用git
命令获取代码版本号、Git提交号,用date
命令获取当前时间
VERSION = $(shell git describe --tags --always)
GIT_COMMIT = $(shell git rev-parse --short HEAD)
BUILD_TIME = $(shell date -R)
4. 在编译时注入版本信息
在Makefile
中,通过-ldflags
选项为go build
命令传入编译参数,实现版本信息的注入
define LDFLAGS
"-X 'github.com/myname/myapp/config.Version=${VERSION}' \
-X 'github.com/myname/myapp/config.GitCommit=${GIT_COMMIT}' \
-X 'github.com/myname/myapp/config.BuildTime=${BUILD_TIME}'"
endef
build:
go build -ldflags ${LDFLAGS} -o myapp_${VERSION} .
5. 验证效果
编译出二进制,即可验证--version
参数;用二进制启动服务进程,即可验证/version
接口。
$ make build
...
//编译过程,省略输出
$ myapp --version
myapp version information:
Version: 0.9.2
Git Commit: b554659
Build Time: Sat, 17 Jun 2023 11:30:44 +0800
Go version: go1.20.5
OS/Arch: darwin/arm64
$ myapp
...
//启动进程,省略输出
$ curl http://127.0.0.1:8080/version | jq
{
"Build Time": "Sat, 17 Jun 2023 11:30:44 +0800",
"Git Commit": "b554659",
"Go Version": "go1.20.5",
"OS/Arch": "darwin/arm64",
"version": "0.9.2"
}
总结
二进制文件直接带上版本信息,能为代码调试和问题定位带来很大的方便。本文介绍了通过-ldflags
选项实现在编译时注入版本信息的具体做法。
这是一个比较通用的方法,很多开源软件比如 Docker 也是这样做的。
转载自:https://juejin.cn/post/7245483735217356855