背景:
在做项目时,遇到一个监控需求,就是每次执行sql语句都要上报耗时到prometheus。首先想到的是在写业务逻辑时每次执行sql语句都自己上报,但这样会侵入业务代码,而且有的人会忘了上报,或者上报格式不规范,所以想到将上报逻辑封装到gorm的框架里。
具体原理:
gorm执行sql语句都是通过注册callback函数实现的。如执行如下语句时:
image
会依次执行、queryCallback、queryCallback、queryCallback,这是因为gorm初始化时注册了这几个函数:
image
如果我们想注册些自己写的函数呢?而且想控制函数调用的顺序,比如想自己注册一个函数func2,而且在queryCallback之后,queryCallback之前调用,那改怎么办?其实可以这样:
image
其中after函数的参数就是注册queryCallback时指定的名字,Register的第一个参数callbackName就是你要注册的函数的名字,callbackName要保持唯一性,否则后面Register的会覆盖前面的。
至此,调first函数时,将会依次调这几个函数:queryCallback、func、queryCallback、queryCallback
源码分析:
callback的定义。上面的first语句会把callback func append到queries字段里。
// Callback is a struct that contains all CRUD callbacks
// Field `creates` contains callbacks will be call when creating object
// Field `updates` contains callbacks will be call when updating object
// Field `deletes` contains callbacks will be call when deleting object
// Field `queries` contains callbacks will be call when querying object with query methods like Find, First, Related, Association...
// Field `rowQueries` contains callbacks will be call when querying object with Row, Rows...
// Field `processors` contains all callback processors, will be used to generate above callbacks in order
type Callback struct {
logger logger
creates []*func(scope *Scope)
updates []*func(scope *Scope)
deletes []*func(scope *Scope)
queries []*func(scope *Scope)
rowQueries []*func(scope *Scope)
processors []*CallbackProcessor
}
这个就是first的源码,最后一行调callCallbacks时就会依次调queries的func。
// First find first record that match given conditions, order by primary key
func (s *DB) First(out interface{}, where ...interface{}) *DB {
newScope := s.NewScope(out)
newScope.Search.Limit(1)
return newScope.Set("gorm:order_by_primary_key", "ASC").
inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
}
func (scope *Scope) callCallbacks(funcs []*func(s *Scope)) *Scope {
defer func() {
if err := recover(); err != nil {
if db, ok := scope.db.db.(sqlTx); ok {
db.Rollback()
}
panic(err)
}
}()
for _, f := range funcs {
(*f)(scope)
if scope.skipLeft {
break
}
}
return scope
}
参考文献:
http://gorm.book.jasperxu.com/callbacks.html