Go Lang - GORM
Published: 2021-04-29 08:46:23

##Preface
In order to facilitate accurate troubleshooting, it is necessary to record the corresponding relationship between the current request information and the currently executed SQL information. The recorded SQL information includes:

The current time of SQL execution;

* File address and line number for executing SQL;

* The time it takes to execute SQL;

* The number of rows affected by SQL execution;

* SQL statement executed;

The database component is used [GORM](https://www.wikiod.com/w/GORM_Introduction).

##Ideas
1. Before executing SQL, set the start execution time (it will be used to calculate the execution time);

2. After executing SQL, first, get the context of the current request, why get the context, because you need to get the request information from the context, second, get the time before SQL execution, to calculate the execution time, third, Getting SQL execution information, and then set the data Tracein Tracea project link packet, it will be introduced later in the article;

Above the need to use GORM two knowledge points Callbacks and Context these two are GORM V2 only the need to import the package gorm.io/gorm.

##Demo code
Context The transfer requires GORM V2 provided With Context() a method.
```
func (u *userRepo) getUserByID(ctx core.Context, id uint) (*user_model.UserDemo, error) {
 data := new(user_model.UserDemo)
 err := u.db.GetDbR().WithContext(ctx).First(data, id).Error
 if err != nil {
  return nil, errors.Wrap(err, "[user_demo] get user data err")
 }
 return data, nil
}
```
Write CallBacks plug-in code, written in GORM Plugin interface is very simple, you only need to implement two methods can be.
```
// Plugin GORM plugin interface
type Plugin interface {
 Name() string
 Initialize(*DB) error
}
```
Below is the plug-in code I wrote:
```
type TracePlugin struct{}

func (op *TracePlugin) Name() string {
 return "tracePlugin"
}

func (op *TracePlugin) Initialize(db *gorm.DB) (err error) {
 // Before starting
 _ = db.Callback().Create().Before("gorm:before_create").Register(callBackBeforeName, before)
 _ = db.Callback().Query().Before("gorm:query").Register(callBackBeforeName, before)
 _ = db.Callback().Delete().Before("gorm:before_delete").Register(callBackBeforeName, before)
 _ = db.Callback().Update().Before("gorm:setup_reflect_value").Register(callBackBeforeName, before)
 _ = db.Callback().Row().Before("gorm:row").Register(callBackBeforeName, before)
 _ = db.Callback().Raw().Before("gorm:raw").Register(callBackBeforeName, before)

 // after the end
 _ = db.Callback().Create().After("gorm:after_create").Register(callBackAfterName, after)
 _ = db.Callback().Query().After("gorm:after_query").Register(callBackAfterName, after)
 _ = db.Callback().Delete().After("gorm:after_delete").Register(callBackAfterName, after)
 _ = db.Callback().Update().After("gorm:after_update").Register(callBackAfterName, after)
 _ = db.Callback().Row().After("gorm:row").Register(callBackAfterName, after)
 _ = db.Callback().Raw().After("gorm:raw").Register(callBackAfterName, after)
 return
}

var _ gorm.Plugin = &TracePlugin{}

func before(db *gorm.DB) {
 db.InstanceSet(startTime, time.Now())
 return
}

func after(db *gorm.DB) {
 _ctx := db.Statement.Context
 ctx, ok := _ctx.(core.Context)
 if !ok {
  return
 }

 _ts, isExist := db.InstanceGet(startTime)
 if !isExist {
  return
 }

 ts, ok := _ts.(time.Time)
 if !ok {
  return
 }

 sql := db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)

 sqlInfo := new(trace.SQL)
 sqlInfo.Timestamp = time_parse.CSTLayoutString()
 sqlInfo.SQL = sql
 sqlInfo.Stack = utils.FileWithLineNum()
 sqlInfo.Rows = db.Statement.RowsAffected
 sqlInfo.CostSeconds = time.Since(ts).Seconds()
 ctx.Trace().AppendSQL(sqlInfo)

 return
}
```
Finally, use this plug-in when db is connected:
```
// Use plugin
db.Use(&TracePlugin{})
```
##summary

It is written in trace part of the package, this package can record information (JSON format):

Support setting trace_id

Support setting request information

Support setting response information

Support setting third_party_requests to request information

Support setting debugs to print debugging information

Support setting sqls to execute SQL information

Can record cost_seconds execution time

The above code is in the go-gin-api project, address:

github.com/xinliangnote/go-gin-api

See more [Gorm tutorial](https://www.wikiod.com/w/GORM_Introduction)