golang增加预编译信息

问题

在日常的使用中,我们主要是使用git来进行代码的管理。虽然平时在开发时方便了,但是当bin文件分发到测试环境或者线上环境之后有了问题,我们回头再来找相应代码就显得无能为力了,最近两天在看Prometheus项目的启动信息时发现它会打出十分详细的编译信息,包括:

  • 编译器版本
  • branch
  • git的md5
  • 编译日期
  • 编译用户&机器

如下:

1
2
level=info ts=2018-02-12T08:46:09.8333802Z caller=main.go:215 msg="Starting Prometheus" version="(version=2.0.0, branch=HEAD, revision=0a74f98628a0463dddc90528220c94de5032d1a0)"
level=info ts=2018-02-12T08:46:09.833986917Z caller=main.go:216 build_context="(go=go1.9.2, user=root@615b82cb36b6, date=20171108-07:41:08)"

我们可以看到,从输出中我们能非常准确的定位到该bin文件对应的git代码,是不是很方便?方向找到了,那么就开始想办法加到自己代码里面吧

调研

通过关键字定位到了代码文件,但是比较奇怪的是在代码中并没有对以上相关变量进行赋值的地方。想了一下就想明白了,像编译信息,应该是在编译时候进行初始化,之后再怎么运行都不会再对变量进行相关操作了,所以这些变量应该是在文件中进行了声明,但是在编译的时候进行了初始化的。经过了漫长的定位终于在阴差阳错的情况下看到了如下语句:

1
build -o xxx -ldflags -X xxx.Version=0.15.0 -X xxx.Revision=9a5bd5f8e4b9c609c46af1496b415fc0667b9d83 -X xxx.Branch=master -X xxx.BuildUser=shadage@mt -X xxx.BuildDate=20180212-05:31:05

看到了吗,在build的阶段对包里面的几个变量进行了赋值,这里使用的是配套的打包工具比较复杂这里不做详细介绍了,到这里我们就知道应该怎么做了,就是在build的时候增加上-ldflags ‘’-X package.Variable=1234’这种方式进行变量的初始化就ok啦,这里需要注意下加上单引号,因为这个选项最终是要传递给连接器的

实现

方法已经很明确了,只需要在编译的时候进行以上信息的获取,并且在编译的时候把这些信息传递给编译器就ok了,一下给出一个简单的实现:

1
2
3
4
5
6
7
#获取代码版本信息
Revision=$(git rev-parse HEAD)
Branch=$(git rev-parse --abbrev-ref HEAD)
BuildDate=$(date +'%Y%m%d-%H:%M:%d')
BuildUser=$(whoami)@$(hostname)
go build -ldflags "-X $PACKPATH/g.Branch=$Branch -X $PACKPATH/g.Revision=$Revision -X $PACKPATH/g.BuildUser=$BuildUser -X $PACKPATH/g.BuildDate=$BuildDate"
#在打包的时候将变量传递过去就ok了,这里还有一点需要注意的是路径要使用相对路径,和import当中的路径需要保持一致,否则无效
1
2
3
4
5
6
7
8
9
//go版本通过runtime模块获取
var (
VERSION string = "2.1.2"
Revision string
Branch string
BuildUser string
BuildDate string
GoVersion = runtime.Version()
)

以上,就达到了获取bin文件对应代码信息的目的。

当然啦,以上只是一种方法,一种比较正统的方法,对于不知道编译器、连接器这种底层的同学其实也是可以实现的,例如,我在代码中使用关键字对以上变量进行标示,在大宝之前获取到了git等信息之后,我再使用sed修改一些代码文件也是ok的,只是比较野啦:)毕竟条条大路通罗马,方法不重要,目的达到才是最重要的。

hello http://just4fun.im/2018/02/01/golang增加预编译信息/ bye