xgo trace: 一个强大的Go堆栈可视化工具
概述
xgo提供了一系列工具,可以帮助Go开发者更快、更轻松地编写单元测试。
这些工具包括:
- Trace
- Mock
- Trap
本文将介绍Trace工具。
如果你还不知道, xgo项目地址: https://github.com/xhd2015/xgo。
关于Trace工具
xgo中的Trace功能可用于可视化Go程序的执行过程。
在某些情况下,这可以替代调试器,减少查找错误所需的工作量。
问题背景
当我们开始接触一个新项目时,我们很难快速了解整个程序在底层做了什么。仅通过运行一些测试并观察结果是不够的。
xgo提供了Trace工具来解决这个问题。
为了展示Trace如何帮助我们快速理解代码,我将从几天前Reddit用户提出的一个随机项目开始,该项目可以在https://www.reddit.com/r/golang/s/RpEtEApIUj找到。
克隆仓库并运行一些测试
这里所选择的项目是pocketbase。
让我们按照下面的步骤开始:
- 克隆仓库
git clone https://github.com/pocketbase/pocketbase.git
cd pocketbase
- 运行
apis/backup_test.go
的测试
go test -v -run TestBackupsList ./apis
输出:
=== RUN TestBackupsList
=== PAUSE TestBackupsList
=== CONT TestBackupsList
=== RUN TestBackupsList/unauthorized
=== RUN TestBackupsList/authorized_as_auth_record
=== RUN TestBackupsList/authorized_as_admin_(empty_list)
=== RUN TestBackupsList/authorized_as_admin
--- PASS: TestBackupsList (0.83s)
--- PASS: TestBackupsList/unauthorized (0.23s)
--- PASS: TestBackupsList/authorized_as_auth_record (0.29s)
--- PASS: TestBackupsList/authorized_as_admin_(empty_list) (0.08s)
--- PASS: TestBackupsList/authorized_as_admin (0.23s)
PASS
ok github.com/pocketbase/pocketbase/apis 2.483s
一切顺利,让我们使用xgo trace来可视化上述测试的堆栈信息。
第一次尝试使用Trace
xgo的Trace功能不需要任何依赖, 这是有意设计如此的,因此你不需要修改项目的任何内容,即可使用。
首先,安装xgo工具:
go install github.com/xhd2015/xgo/cmd/xgo@latest
然后,只需将go test
替换为xgo test
,并添加一个--strace
标志(缩写为stack trace):
xgo test -v -run TestBackupsList --strace ./apis
输出:
xgo is taking a while to setup, please wait...
=== RUN TestBackupsList
=== PAUSE TestBackupsList
=== CONT TestBackupsList
=== RUN TestBackupsList/unauthorized
=== RUN TestBackupsList/authorized_as_auth_record
=== RUN TestBackupsList/authorized_as_admin_(empty_list)
=== RUN TestBackupsList/authorized_as_admin
--- PASS: TestBackupsList (1.17s)
--- PASS: TestBackupsList/unauthorized (0.35s)
--- PASS: TestBackupsList/authorized_as_auth_record (0.35s)
--- PASS: TestBackupsList/authorized_as_admin_(empty_list) (0.13s)
--- PASS: TestBackupsList/authorized_as_admin (0.33s)
PASS
ok github.com/pocketbase/pocketbase/apis 3.196s
每个测试对应一个生成的JSON trace文件:
$ ls ./apis/TestBackupsList
authorized_as_admin.json
authorized_as_auth_record.json
authorized_as_admin_(empty_list).json
unauthorized.json
查看TestBackupsList/unauthorized
的堆栈信息:
$ xgo tool trace apis/TestBackupsList/unauthorized.json
Server listen at http://localhost:7070
浏览器自动打开,并显示如下内容:
我们便得到了可视化的堆栈跟踪!
通过堆栈跟踪理解代码
如上面的堆栈跟踪截图所示:
- 左侧是调用树,每个项表示一个函数调用,包括:
- 每个项前面的颜色标识
- 深蓝色表示函数正常返回
- 红色表示函数返回了一个错误
- 尾部灰色的时间表示函数执行时间
- 每个项前面的颜色标识
- 右侧是所选函数的包路径、请求和响应
- 函数名称旁边有一个VSCode图标
- 单击该图标将打开VSCode,并定位到函数定义的位置
对于上面这个案例,我们可以看到一串红色矩形的树形结构,这表明有个函数发生了错误。
由于错误通常会从下往上传递,我们可以直接定位到错误的根本原因:
这个函数响应了以下内容:
error:The request requires valid admin authorization token to be set.
点击VSCode图标查找原因,定位到代码apis/middlewares.go#L116:
// RequireAdminAuth middleware requires a request to have
// a valid admin Authorization header.
func RequireAdminAuth() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { <--- 定位到这一行
admin, _ := c.Get(ContextAdminKey).(*models.Admin)
if admin == nil {
return NewUnauthorizedError("The request requires valid admin authorization token to be set.", nil)
}
return next(c)
}
}
}
很明显,admin, _ := c.Get(ContextAdminKey).(*models.Admin)
这一行返回了 nil
,所以返回了错误。
因此,在程序执行的某个步骤中,肯定会将 *models.Admin
注入到上下文中。
通过查看Trace,我们找到了这个函数:
让我们通过点击VSCode图标来查看其代码,这将带我们到 apis/middlewares.go#L219:
func LoadAuthContext(app core.App) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { <--- 定位到这一行
token := c.Request().Header.Get("Authorization")
if token == "" {
return next(c)
}
// the schema is not required and it is only for
// compatibility with the defaults of some HTTP clients
token = strings.TrimPrefix(token, "Bearer ")
...
switch tokenType {
case tokens.TypeAdmin:
...
if err == nil && admin != nil {
c.Set(ContextAdminKey, admin)
}
...
}
}
}
显然,这个函数的指责就是解析Authorizationt, 并将对应的信息*models.Admin
注入到上下文中。
现在我们知道,这个测试的主要目的,就是确保如果当前用户不是管理员,但需要执行仅限管理员的操作时的情况,它应当失败。
与传统堆栈跟踪的比较
与Go运行时生成的传统堆栈相比,xgo生成的trace提供了更详细的信息。
xgo trace包括调用树、函数持续时间和包路径,可以全面了解程序的执行过程。
相比之下,传统堆栈只提供了在生成堆栈时的程序快照,不包含有关程序执行的完整信息。
xgo trace还提供详细的请求和响应记录,更容易分析程序的行为。
总结
在这个示例中,我们演示了xgo trace工具在理解新项目时的帮助。这个示例可以在这里查看:https://blog.xhd2015.xyz/trace-pocketbase.html。
但xgo trace的好处不仅仅是帮助我们快速理解代码,在某些情况下,它还可以替代Delve调试。
如果代码包含多层间接引用,使用Delve调试程序有时会很繁琐。但是,xgo会将所有编译时的间接引用暴露出来,从而获得程序执行的真实路径,并通过查看每个步骤的时间来找到程序可能的瓶颈所在。
好了,这就是全部内容,感谢阅读。
如果您觉得xgo项目有用,请在项目的GitHub页面 https://github.com/xhd2015/xgo上留下评论并给项目star以便更多人能够看到。