需求说明
- 管理后台所有修改,添加,删除的操作都要记录
- 操作日志的统计不影响主程序的性能
需求分析
- 把相关代码封装成中间件,独立使用
- 合理使用goroutine,不影响主线程的性能
文档说明
- 基于golang语言开发
- 基于gin网络框架开发
- 基于MySQL5.8开发
- 把操作日志部分封装成中间件,在rourter文件中引用
- 非核心代码已省略,用3个竖着排列的点号.表示
数据库表结构设置
操作日志表
代码
中间件代码
代码分析
- 我们可以通过context直接获得请求方式和请求的url
- 无法直接获得返回信息,我们可以借助"ResponseWriter",运用拦截器的思想,把返回信息先截取到,再向下继续传递
- 像获得客户端ip这类方法封装到util中,方便灵活调用
- 我们无法将adminLogs()方法整体设置为goroutine,因为这样会将context的事件传递在新的协程中进行,无法正常传递。
- 所以我们再c.Next()事件传递之后,把json解析成结构体,以及保存操作日志到数据库的操作设置为使用goroutine协程操作
- 操作日志是没有比较记录查询操作的,所以我们把请求方式为GET的过滤掉
package middleware //amdin操作日志 import ( . . . "bytes" "encoding/json" "fmt" "github.com/gin-gonic/gin" "strings" ) type bodyLogWriter struct { gin.ResponseWriter bodyBuf *bytes.Buffer } func (w bodyLogWriter) Write(b []byte) (int, error) { w.bodyBuf.Write(b) return w.ResponseWriter.Write(b) } var CommonLogInterceptor = commonLogInterceptor() /* 1 使用goroutine和channel实现操作日志的入库保存,尽可能的不影响主程序 2 goroutine协程,提高并发量 3 channel通道 */ func commonLogInterceptor() gin.HandlerFunc { return func(c *gin.Context) { adminLogs(c) } } //获得每次请求返回的code和message func adminLogs(c *gin.Context) { if admin, _ := c.Get("admin"); admin != nil { method := c.Request.Method url := c.Request.URL.Path strBody := "" var blw bodyLogWriter blw = bodyLogWriter{bodyBuf: bytes.NewBufferString(""), ResponseWriter: c.Writer} c.Writer = blw c.Next() if method != "GET" { strBody = strings.Trim(blw.bodyBuf.String(), "\n") go func(strBody string) { var returnJson api.ReturnJson json.Unmarshal([]byte(strBody), &returnJson) message := fmt.Sprintf("%v", returnJson.Message) adminInfo := admin.(**service.RunningClaims) adminId := (*adminInfo).ID adminName := (*adminInfo).Account var log = model.AdminLog{ AdminId: adminId, AdminName: adminName, Method: method, Url: url, Ip: util.RemoteIP(c.Request), Code: returnJson.Code, Message: message, } model.CreateLog(log) }(strBody) } } }
model层代码
package model type AdminLog struct { ID int `json:"id"` AdminId uint `json:"admin_id"` AdminName string `json:"admin_name"` Method string `json:"method"` Ip string `json:"ip"` Url string `json:"url"` Code int `json:"code"` Message string `json:"message"` } func CreateLog(log AdminLog) { DB.Create(&log) } 复制代码
路由代码
package server import ( . . . "os" "github.com/gin-gonic/gin" ) // NewRouter 路由配置 func NewRouter() *gin.Engine { r := gin.Default() // 其他中间件 . . . // 路由 v1 := r.Group("/api/v1") { v1.POST("login", api.Login) auth := v1.Group("") //登录校验中间件 auth.Use(middleware.AuthRequired()) //关键代码:权限角色校验 auth.Use(middleware.AuthCheckMiddleware) //操作日志 auth.Use(middleware.CommonLogInterceptor) { . . . // 获取所有学校 { auth.GET("/school/", api.GetSchoolInfo) } . . . } } return r } 复制代码
总结
- 以上则是我的实现思路
- 还有另外一种思路,计划已消息队列的方式实现,发送通知进行日志的报错
公众号:程序员升级打怪之旅
微信号:wangzhongyang1993