/** * package middleware * 作用:接口请求日志中间件 * 说明:记录所有API请求信息,异步写入数据库 */ package middleware import ( "bytes" "encoding/json" "io" "strings" "time" "xk-websocket-v2/internal/model" "xk-websocket-v2/internal/utils" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // RequestLogMiddleware 请求日志中间件 type RequestLogMiddleware struct { DB *gorm.DB } // NewRequestLogMiddleware 创建请求日志中间件 func NewRequestLogMiddleware(db *gorm.DB) *RequestLogMiddleware { return &RequestLogMiddleware{DB: db} } // Handler 中间件处理函数 func (m *RequestLogMiddleware) Handler() gin.HandlerFunc { return func(c *gin.Context) { // 跳过OPTIONS请求 if c.Request.Method == "OPTIONS" { c.Next() return } // 获取请求IP ip := getClientIP(c) // 本地IP不记录 if utils.GetIPLocation(ip) == "本地" { c.Next() return } // 获取请求参数 var requestBody []byte if c.Request.Body != nil { requestBody, _ = io.ReadAll(c.Request.Body) c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) } // 获取用户ID(从Context中获取,未登录为"0") userID := "0" if uid, exists := c.Get("user_id"); exists { if uidStr, ok := uid.(string); ok { userID = uidStr } } // 记录开始时间 startTime := time.Now() // 创建响应写入器 writer := &responseWriter{ ResponseWriter: c.Writer, body: &bytes.Buffer{}, } c.Writer = writer // 处理请求 c.Next() // 计算请求时间 duration := time.Since(startTime) // 异步记录日志(避免影响性能) go m.logRequest(c, ip, userID, requestBody, writer.body.Bytes(), writer.status, duration) } } // logRequest 记录请求日志 func (m *RequestLogMiddleware) logRequest(c *gin.Context, ip, userID string, requestBody, responseBody []byte, httpStatus int, duration time.Duration) { // 获取IP归属地 location := utils.GetIPLocation(ip) // 获取响应code(从响应体中解析) responseCode := 0 if len(responseBody) > 0 { // 尝试解析响应体获取code var resp model.ApiResponse if err := json.Unmarshal(responseBody, &resp); err == nil { responseCode = resp.Code } } // 限制请求参数长度(避免存储过大) requestParams := string(requestBody) if len(requestParams) > 5000 { requestParams = requestParams[:5000] + "...(truncated)" } // 限制返回参数长度 responseParams := string(responseBody) if len(responseParams) > 5000 { responseParams = responseParams[:5000] + "...(truncated)" } // 创建日志记录 log := model.ApiRequestLog{ Route: c.FullPath(), IP: ip, IPLocation: location, UserID: userID, Method: c.Request.Method, RequestParams: requestParams, ResponseParams: responseParams, ResponseCode: responseCode, HTTPStatus: httpStatus, RequestTime: time.Now(), } // 异步写入数据库 m.DB.Create(&log) } // getClientIP 获取客户端IP func getClientIP(c *gin.Context) string { // 优先从X-Forwarded-For获取 ip := c.GetHeader("X-Forwarded-For") if ip != "" { // X-Forwarded-For可能包含多个IP,取第一个 ips := strings.Split(ip, ",") if len(ips) > 0 { return strings.TrimSpace(ips[0]) } } // 从X-Real-IP获取 ip = c.GetHeader("X-Real-IP") if ip != "" { return ip } // 从RemoteAddr获取 return c.ClientIP() } // responseWriter 响应写入器(用于捕获响应内容) type responseWriter struct { gin.ResponseWriter body *bytes.Buffer status int } func (w *responseWriter) Write(b []byte) (int, error) { w.body.Write(b) return w.ResponseWriter.Write(b) } func (w *responseWriter) WriteString(s string) (int, error) { w.body.WriteString(s) return w.ResponseWriter.WriteString(s) } func (w *responseWriter) WriteHeader(statusCode int) { w.status = statusCode w.ResponseWriter.WriteHeader(statusCode) }