Files
goalfylearning-admin/internal/api/middlewares/api_log_middleware.go

142 lines
3.4 KiB
Go

package middlewares
import (
"bytes"
"io"
"strings"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"goalfymax-admin/internal/models"
"goalfymax-admin/pkg/utils"
)
// API日志中间件 - 记录所有接口的调用信息
func APILogMiddleware(logger *utils.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
// 排除健康检查接口
if c.Request.URL.Path == "/health" {
c.Next()
return
}
// 记录开始时间
startTime := time.Now()
// 读取请求体(需要保存以便后续使用)
var requestBody []byte
if c.Request.Body != nil {
requestBody, _ = io.ReadAll(c.Request.Body)
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
}
// 获取用户信息
userID := 0
userEmail := "unknown"
if userInfo, exists := c.Get("user"); exists {
if user, ok := userInfo.(*models.UserInfo); ok && user != nil {
if user.Email != "" {
userEmail = user.Email
} else if user.PreferredUsername != "" {
userEmail = user.PreferredUsername + "@goalfy.com"
}
// 尝试获取用户ID
if id, exists := c.Get("user_id"); exists {
switch v := id.(type) {
case int:
userID = v
case uint:
userID = int(v)
}
}
}
}
// 创建响应写入器包装器以捕获响应
responseWriter := &responseBodyWriter{
ResponseWriter: c.Writer,
body: &bytes.Buffer{},
}
c.Writer = responseWriter
// 处理请求
c.Next()
// 计算耗时
duration := time.Since(startTime)
// 获取响应信息
responseCode := c.Writer.Status()
responseBody := responseWriter.body.String()
// 限制响应体大小(避免打印过大的响应)
if len(responseBody) > 1000 {
responseBody = responseBody[:1000] + "...(truncated)"
}
// 限制请求体大小
requestBodyStr := string(requestBody)
if len(requestBodyStr) > 1000 {
requestBodyStr = requestBodyStr[:1000] + "...(truncated)"
}
// 获取请求信息
method := c.Request.Method
path := c.Request.URL.Path
// 提取模块名称
module := extractModule(path)
// 打印日志
logger.Info("📝 [API日志] 接口调用记录",
zap.String("method", method),
zap.String("path", path),
zap.String("full_path", c.Request.URL.String()),
zap.String("module", module),
zap.Int("user_id", userID),
zap.String("user_email", userEmail),
zap.String("ip_address", c.ClientIP()),
zap.String("user_agent", c.Request.UserAgent()),
zap.String("request_body", requestBodyStr),
zap.Int("response_code", responseCode),
zap.String("response_body", responseBody),
zap.Duration("duration", duration),
zap.Int64("duration_ms", duration.Milliseconds()),
zap.String("status", getStatus(responseCode)),
)
}
}
// responseBodyWriter 用于捕获响应体
type responseBodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w *responseBodyWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
// extractModule 从路径提取模块名称
func extractModule(path string) string {
parts := strings.Split(strings.Trim(path, "/"), "/")
if len(parts) >= 3 {
return parts[2] // /api/admin/{module}
}
return "unknown"
}
// getStatus 根据响应码获取状态
func getStatus(code int) string {
if code >= 200 && code < 300 {
return "success"
} else if code >= 400 && code < 500 {
return "client_error"
} else if code >= 500 {
return "server_error"
}
return "unknown"
}