写点什么

你用 Go 写过中间件吗?带你用 Go 实现【操作日志中间件】

作者:王中阳Go
  • 2022-10-18
    北京
  • 本文字数:1921 字

    阅读完需:约 1 分钟

你用Go写过中间件吗?带你用Go实现【操作日志中间件】

需求说明

  1. 管理后台所有修改,添加,删除的操作都要记录

  2. 操作日志的统计不影响主程序的性能

需求分析

  1. 把相关代码封装成中间件,独立使用

  2. 合理使用 goroutine,不影响主线程的性能

文档说明

  1. 基于 golang 语言开发

  2. 基于 gin 网络框架开发

  3. 基于 MySQL5.8 开发

  4. 把操作日志部分封装成中间件,在 rourter 文件中引用

  5. 非核心代码已省略,用 3 个竖着排列的点号.表示

数据库表结构设置

操作日志表

代码

中间件代码

代码分析

  1. 我们可以通过 context 直接获得请求方式和请求的 url

  2. 无法直接获得返回信息,我们可以借助"ResponseWriter",运用拦截器的思想,把返回信息先截取到,再向下继续传递

  3. 像获得客户端 ip 这类方法封装到 util 中,方便灵活调用

  4. 我们无法将 adminLogs()方法整体设置为 goroutine,因为这样会将 context 的事件传递在新的协程中进行,无法正常传递。

  5. 所以我们再 c.Next()事件传递之后,把 json 解析成结构体,以及保存操作日志到数据库的操作设置为使用 goroutine 协程操作

  6. 操作日志是没有比较记录查询操作的,所以我们把请求方式为 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和messagefunc 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}
复制代码

总结

  1. 以上则是我的实现思路

  2. 还有另外一种思路,计划已消息队列的方式实现,发送通知进行日志的报错

相关文章

  1. GO 部分打算做成一个系列,最终把封装好的代码开源出来

  2. 上一篇:你用 Go 写过中间件吗?带你用 Gin 实现【用户角色权限管理中间件】

一起学习,升级打怪

我们搞了一个对学 Go 真正有帮助的群,欢迎加入:


公众号:程序员升级打怪之旅


微信号:wangzhongyang1993

发布于: 刚刚阅读数: 3
用户头像

王中阳Go

关注

公众号:程序员升级打怪之旅 2022-10-09 加入

微信:wangzhongyang1993

评论 (1 条评论)

发布
用户头像
我们无法将adminLogs()方法整体设置为goroutine,因为这样会将context的事件传递在新的协程中进行,无法正常传递。 如果有更好的实现思路,可以私信我或者在文章底部留言。
刚刚 · 北京
回复
没有更多了
你用Go写过中间件吗?带你用Go实现【操作日志中间件】_golang_王中阳Go_InfoQ写作社区