"go build -X" 的妙用
不知道大家还记不记得,上次发了一篇关于 panic 检测机器人的文章,原理非常简单,简单回顾一下:
业务服务在 recover 函数里通过 HTTP 请求的方式向机器人上报 panic 栈信息。 机器人解析出 panic 栈里的代码行号,调用 gitlab 接口拿到该行代码的提交人、提交日期等信息。
当然,后面我又给机器人增加了一些其他的功能,例如自动拉群,自动提醒相关人修复 panic 代码等……
上面说的这些其实都很好实现,主要就是和飞书 API 打交道,再加上一些逻辑串连一下流程。目前机器人上报了 1000+ 次 panic,工作状态良好。
但偶尔还是有一些小问题的存在,例如有人用开发分支(非 master)上到线上测试环境(只读环境)测试一把,这时机器人还是用 master(默认)分支请求 gitlab 接口拿 commit 信息,拿到的信息就有可能不准。
那有没有什么好的方法能拿到正在运行的进程的代码分支呢?如果能拿到,机器人用代码路径+代码行号+代码分支,就可以从 gitlab 拿到正确的 commit 信息。
答案是有,通过 go build -X
注入。
那具体怎么玩的呢,通过一个小例子来说明。
下面是 build.sh
的代码:
#!/bin/sh
COMMIT_ID=`git log |head -n 1| awk '{print $2;}'`
AUTHOR=`git log |head -n 3| grep Author| awk '{print $2;}'`
BRANCH_NAME=`git branch | awk '/\*/ { print $2; }'`
SERVICE_INFO="$COMMIT_ID,$AUTHOR,$BRANCH_NAME"
echo $SERVICE_INFO
go build -ldflags "-X codebase/build-x/compile_info.ServiceInfo=$SERVICE_INFO" -o output/bin/build
第 3、4、5 行分别用 git 命令拿到本次提交的 commit-id,author,分支名;第 6 行用 “,” 将三者组合成一个字符串;第 8 行用 go build
命令,设置 ldflags,将变量 $SERVICE_INFO
注入到包变量 codebase/build-x/compile_info.ServiceInfo
,这样在 Go 代码中就可以直接用了。
再看看我的 compile_info 包的代码,非常简单,就定义了一个变量:
package compile_info
var ServiceInfo string
执行完 go build
命令后,compile_info.ServiceInfo 就会被赋上值,在 main 函数里打印一下:
package main
import (
"fmt"
"codebase/build-x/compile_info"
)
func init() {
fmt.Println("init: ", compile_info.ServiceInfo)
}
func main() {
fmt.Println(compile_info.ServiceInfo)
}
先执行:
sh build.sh
再执行:
~/go/src/codebase/build-x$ ./output/bin/build
得到运行结果:
init: 9699dcaae31e7e5eab55a1d75283a6d7158a64e8,raoquancheng,master
9699dcaae31e7e5eab55a1d75283a6d7158a64e8,raoquancheng,master
可知,在 init 函数里我们就可以拿到 compile_info.ServiceInfo 的值了。
代码文件结构如下:
原理也没啥可探究的,就是通过 -ldflags 给链接器传参数:
-X definition: 添加形式为 importpath.name=value 的字符串值定义
其他的一些常见的命令用处:
-s
的作用是去掉符号信息。-w
的作用是去掉调试信息。go build -ldflags "-s -w" -o xxx
之前看到公司项目里 build 脚本里的一些命令不知道有啥用,真正到了用的时候才惊呼:原来是这样!
今天的 go build
妙用你学会了吗?也许下次就可以在同事面前装 B 了,当然如果碰到了老司机,也可能会被打脸。
推荐阅读