• Go语言程序编译注入版本信息,提升Debug速度!


    背景

    为了对运行程序进行版本追踪,避免各个版本的服务程序混淆,通常的做法为在go build编译的时候程序注入
    版本标志、编译时间、当前分支、当前 Tag、当前的提交号等信息,让go程序在运行的时候就可以打印编译时候的参数情况。

    go build -ldflags命令

    ldflags简介

    golang项目常用三个参数,含义如下:

      -X definition #注入变量定义
        	add string value definition of the form importpath.name=value
      -s	disable symbol table # 禁用符号表
      -w	disable DWARF generation # 禁用调试信息
    
    • 1
    • 2
    • 3
    • 4

    -s -w俩个参数可以减小构建二进制包大小。

    go build 命令

      CGO_ENABLED=0 go build \
         -ldflags " \
         -X 'main.BuildVersion=${build_version}' \
         -X 'main.BuildGoVersion=${go_version}' \
         -X 'main.BuildTime=${build_time}' \
         -X 'main.BuildCommit=${build_commit}' \
         -o ${APP_NAME}
         " main.go
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    CGO_ENABLED=0 禁用cgo。

    -X ‘main.BuildVersion=${build_version}’ 编译时候会想main中注册变量BuildVersion,如下代码中main.go代码即可接受。

    代码

    package main
    
    import (
    	"flag"
    	"fmt"
    )
    
    // 构建版本信息
    var (
    	BuildVersion   = ""
    	BuildGoVersion = ""
    	BuildCommit    = ""
    	BuildTime      = ""
    )
    
    func main() {
    
    	// 构建信息,golang版本 commit id 时间
    	var version bool
    	flag.BoolVar(&version, "v", false, "version")
    	flag.Parse()
    	if version {
    		fmt.Printf("go version: %s\n,Build version: %s\n, Build commit: %s\n, Build time: %s\n",
    			BuildGoVersion, BuildVersion, BuildCommit, BuildTime)
    		return
    	}
    	// 业务逻辑
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    编译后结果运行如下

    $ ./demo_app -v         
    go version: go version go1.19.2 darwin/amd64,
    Build version: dev_8899bca, 
    Build commit: 8899bca, 
    Build time: 2022-11-21T15:12:57Z
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Makefile

    简介

    Makefile简单理解为它定义了一个项目文件的编译规则。早期适用于 C/C++ 工程的编译,一旦写编写好 Makefile 文件,
    只需要一个 make 命令,整个工程就开始自动编译,不再需要手动执行 GCC 命令。
    后面随着逐渐发展其它项目也开始使用Makefile进行服务的编译,极大简化项目编译过程。

    所以我们将上文的go build命令集合进Makefile文件中,简化编译过程。

    Makefile结构说明

    Makefile里主要包含了五个东西:变量定义、显式规则、隐晦规则、文件指示和注释。

    1. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
    2. 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 刚才写的疑似shell脚本的Makefile全部都是显示规则。
    3. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
    4. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样。
    5. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符。

    实践

    获取仓库taggit describe --tags --abbrev=0

    获取仓库最近提交commit id
    git rev-parse --short HEAD

    获取仓库当前所处分支git rev-parse --abbrev-ref HEAD

    在go项目仓库根目录下 创建Makefile文件

    git_rev    = $(shell git rev-parse --short HEAD)
    git_tag    = $(shell git describe --tags --abbrev=0)
    git_branch = $(shell git rev-parse --abbrev-ref HEAD)
    app_name   = "demo_app"
    
    BuildVersion := $(git_branch)_$(git_rev)
    BuildTime := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
    BuildCommit := $(shell git rev-parse --short HEAD)
    BuildGoVersion := $(shell go version)
    
    # in detached HEAD state
    ifeq ($(git_branch), HEAD)
    	git_branch = $(shell git show-ref | grep $(shell git show HEAD | sed -n 1p | cut -d " " -f 2) | sed 's|.*/\(.*\)|\1|' | grep -v HEAD | sort | uniq | head -n 1)
    	# when git checkout <>, branch may still be empty
    	ifeq ($(git_branch), )
    		git_branch := $(git_tag)
    	endif
    	BuildVersion := $(git_branch)_$(git_rev)
    endif
    
    ifeq ($(git_branch), develop)
    	BuildVersion := develop_$(git_rev)
    endif
    
    ifeq ($(git_branch), master)
    	BuildVersion := release_$(git_tag)_$(git_rev)
    endif
    
    # -ldflag 参数
    GOLDFLAGS = -s -w -X 'main.BuildVersion=$(BuildVersion)'
    GOLDFLAGS += -X 'main.BuildTime=$(BuildTime)'
    GOLDFLAGS += -X 'main.BuildCommit=$(BuildCommit)'
    GOLDFLAGS += -X 'main.BuildGoVersion=$(BuildGoVersion)'
    
    # go mod
    mod:
    	go mod tidy
    
    all: linux
    
    # linux 构建二进制
    linux:
    	@GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o "$(app_name)" -ldflags "$(GOLDFLAGS)"
    	
    # arm 构建二进制
    arm:
    	@GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o "$(app_name)" -ldflags "$(GOLDFLAGS)"
    	
    # mac 构建二进制
    mac:
    	@GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o "$(app_name)" -ldflags "$(GOLDFLAGS)"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    make linux 即可生成demo_app二进制服务

    编译后结果运行如下,与go build之后结果一样

    $ ./demo_app -v         
    go version: go version go1.19.2 darwin/amd64,
    Build version: dev_8899bca, 
    Build commit: 8899bca, 
    Build time: 2022-11-21T15:12:57Z
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    小结

    通过makefile与-ldflags结合注入服务版本信息,是一种常见方式,可以帮助我们快速定位一些基础问题,避免踩坑。

  • 相关阅读:
    《windows 程序设计》读书笔记 三
    信息学奥赛一本通:1399:甲流病人初筛
    java毕业设计糖助手服务交流平台mybatis+源码+调试部署+系统+数据库+lw
    SpringBoot启动源码-初了解
    前端的多种克隆方式和注意事项
    登录安全分析报告:小米官网注册
    第十章 JavaScript操作BOM对象
    解决&nbsp;不生效的问题
    VMware-Linux切换桥接模式上网教程(超详细)
    【免费模板】2023数学建模国赛word+latex模板免费分享
  • 原文地址:https://blog.csdn.net/Merlin_feng/article/details/127991059