• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Go编写中间件吗, 用Gin实现【操作日志中间件】

武飞扬头像
juejin
帮助113

需求说明

  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. 还有另外一种思路,计划已消息队列的方式实现,发送通知进行日志的报错

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfbagf
系列文章
更多 icon
同类精品
更多 icon
继续加载