不知道大家还记不记得,上次发了一篇关于 panic 检测机器人的文章,原理非常简单,简单回顾一下:

  1. 业务服务在 recover 函数里通过 HTTP 请求的方式向机器人上报 panic 栈信息。
  2. 机器人解析出 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 了,当然如果碰到了老司机,也可能会被打脸。

参考资料

【ldflags go version】 https://ms2008.github.io/2018/10/08/golang-build-version/

【go build 命令】 https://github.com/hyper0x/go_command_tutorial/blob/master/0.1.md

【Go 编译命令】 https://chenwenke.cn/blog/2019/11/05/2019-11-05-go-build/