zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Go gin框架封装中间件之2:操作日志中间件

2023-06-13 09:14:00 时间

‍需求说明

  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和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
}

总结

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

相关文章

  1. GO部分打算做成一个系列,最终把封装好的代码开源出来
  2. 上一篇:你用Go写过中间件吗?带你用Gin实现【用户角色权限管理中间件】

联系我

如果有更好的实现思路,或者项目合作,可以私信我或者在文章底部留言。