
Name
go-tingyun-mixin
一个为 Golang Web MVC 项目添加听云探针的代码批量修改工具
Synopsis
./go-tingyun-mixin <code root path> <root package> <framework>
# eg: ./go-tingyun-mixin "/path/to/src/github.com/somebody/some-project" "github.com/somebody/some-project" gin
# <code root path>: 项目代码的根目录
# <root package>: 项目的根包名
# <framework>: 项目使用的框架,目前支持 gin, beego
Description
此工具会递归扫描根目录下所有 .go
后缀的文件,除了名为 vender
的目录下的所有文件
被扫描的 .go
文件可能会被此工具改写,所以请注意在使用前务必使用代码版本控制工具做好备份!
所有添加的代码中用到的包也会在 import
中自动添加
tingyun.AppInit
等全局代码请自行添加
component
此工具会扫描所有被标记为 component 的函数:
package some_service
import (
...
)
//@tingyun:component
func FunctionInService(i int) int {
...
j := SubFunctionInService(i)
...
}
//@tingyun:component
func SubFunctionInService(i int) int {
...
}
将其改写为:
package some_service
import (
tingyun "github.com/TingYunAPM/go"
...
)
//@tingyun:component
func FunctionInService(i int, tyComponent *tingyun.Component) int {
tyComponentSub := tyComponent.CreateComponent("FunctionInService")
defer tyComponentSub.Finish()
...
j := SubFunctionInService(i, tyComponentSub)
...
}
//@tingyun:component
func SubFunctionInService(i int, tyComponent *tingyun.Component) int {
tyComponentSub := tyComponent.CreateComponent("FunctionInService")
defer tyComponentSub.Finish()
...
}
标记
在函数定义上方添加注释作为标记:
//@tingyun:component
func ...
标记的函数不能带有接收器参数,即不能是任何类型的方法,否则不会被识别为 component 函数
可以与其它注释放在一起,但注释与注释之间不能有空行(/*
形式的注释中的空行不算注释之间的空行):
// other comments
//@tingyun:component
// other comments
/* other
comments */
修改形参
component 函数的形参会在最后追加一个参数: tyComponent *tingyun.Component
因此,如果在其它函数中调用了此 component 函数,那么那些函数也必须标记为 component 函数,这样才能传递 *tingyun.Component
;一直上溯到 controller 函数,只有 controller 函数不标记为 component 函数
修改函数体
会在函数体的开头添加以下代码:在实参 component 名下定义一个此函数对应的子 component,其名称为函数名;并且 defer finish
tyComponentSub := tyComponent.CreateComponent("FunctionInService")
defer tyComponentSub.Finish()
如果不想在该函数内创建子 component,而只是需要把实参 component 透传下去,可以将标记改为 @tingyun:component,through
修改实参
component 函数中对其它 component 函数的调用实参也会被改写,在最后追加传入 tyComponentSub
;会递归扫描函数体主块、子块、if 块、for 块、switch 块、case 子句内的语句;如果标记为 @tingyun:component,through
,则最后追加传入的变量名为 tyComponent
直接的调用表达式会被识别:
SubFunctionInService(...)
修改为:
SubFunctionInService(..., tyComponentSub)
如果调用存在于赋值表达中,则赋值等号的右边只能有一项,且 =
和 :=
赋值均会被识别:
j := SubFunctionInService(...)
修改为:
j := SubFunctionInService(..., tyComponentSub)
如果调用的是其它包中的 component 函数:必须以 <包名>.<函数名> 的形式调用,即必须有且只有一个点号;必须是本项目中的包,即包名以命令行参数 <root package>
开头;<包名> 可以使用 import 中的别名:
another_service.FunctionInService(...)
修改为:
another_service.FunctionInService(..., tyComponentSub)
controller
首先以 gin 为例,此工具会扫描所有被标记为 controller 的函数:
package some_controller
import (
"github.com/gin-gonic/gin"
"github.com/somebody/some-project/services/some_service"
)
//@tingyun:controller
func Get(ginCtx *gin.Context) {
...
j := some_service.FunctionInService(1)
...
}
将其改写为:
package some_controller
import (
"github.com/gin-gonic/gin"
"github.com/somebody/some-project/services/some_service"
tingyun "github.com/TingYunAPM/go"
...
)
//@tingyun:controller
func Get(ginCtx *gin.Context) {
tyAction := tingyun_gin.FindAction(ginCtx)
tyComponent := tyAction.CreateComponent("Get")
defer tyComponent.Finish()
...
j := some_service.FunctionInService(1, tyComponent)
...
}
标记
在函数定义上方添加注释作为标记:
//@tingyun:controller
func ...
形参表中必须至少带有类型为 *gin.Context
的参数,否则不会被识别为 controller 函数
同样,可以与其它注释放在一起,但注释与注释之间不能有空行
修改函数体
会在函数体的开头添加以下代码,从 *gin.Context
查找 action,*gin.Context
的变量名和函数定义中的保持一致;在 action 名下定义一个此函数对应的 component,其名称为函数名;并且 defer finish
tyAction := tingyun_gin.FindAction(ginCtx)
tyComponent := tyAction.CreateComponent("Get")
defer tyComponent.Finish()
修改实参
controller 函数中对 component 函数的调用实参也会被改写,在最后追加传入 tyComponent
,规则与 component 函数相同
beego
与 gin 的处理不同的是,对 controller 函数的要求为,必须包含一个接收器参数:
func (this *SomeController) Get() {
action 的定义不同,其中的 this
与接收器参数名保持一致:
tyAction := tingyun_beego.FindAction(this.Ctx)