package middleware import ( "net/http" "time" "hfb_sys/backend/pkg/response" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // 正常请求不记录访问日志;只有慢请求、服务端错误、限流和有诊断价值的认证失败会输出。 const slowRequestThresholdMs = 500 func RequestLogger(logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() latencyMs := float64(time.Since(start).Microseconds()) / 1000 status := c.Writer.Status() path := c.Request.URL.Path route := c.FullPath() authFailure := meaningfulAuthFailure(c) if shouldSkipHTTPLog(path, route, status, latencyMs) && !authFailure { return } fields := []zap.Field{ zap.String("request_id", GetRequestID(c)), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("route", route), zap.Int("status", status), zap.String("code", response.CodeFromContext(c)), zap.Float64("duration_ms", latencyMs), zap.String("client_ip", c.ClientIP()), } if userID, ok := c.Get(ContextUserID); ok { fields = append(fields, zap.Any("user_id", userID)) } if adminID, ok := c.Get(ContextAdminID); ok { fields = append(fields, zap.Any("admin_id", adminID)) } if reason, ok := c.Get(ContextAuthFailureReason); ok { fields = append(fields, zap.Any("auth_failure_reason", reason)) } if source, ok := c.Get(ContextAuthTokenSource); ok { fields = append(fields, zap.Any("auth_token_source", source)) } if detail, ok := c.Get(ContextAuthFailureDetail); ok { fields = append(fields, zap.Any("auth_failure_detail", detail)) } if tokenVersion, ok := c.Get(ContextAuthTokenVersion); ok { fields = append(fields, zap.Any("auth_token_version", tokenVersion)) } if currentVersion, ok := c.Get(ContextAuthCurrentVersion); ok { fields = append(fields, zap.Any("auth_current_token_version", currentVersion)) } if len(c.Errors) > 0 { fields = append(fields, zap.String("error", c.Errors.Last().Err.Error()), zap.Int("error_count", len(c.Errors)), ) } if stack, ok := c.Get(contextPanicStack); ok { fields = append(fields, zap.Any("stack", stack)) } switch { case status >= 500: logger.Error("HTTP 请求失败", fields...) case status == http.StatusTooManyRequests: logger.Warn("HTTP 请求被限流", fields...) case authFailure: logger.Warn("后台认证失败", fields...) case latencyMs >= slowRequestThresholdMs: logger.Warn("HTTP 慢请求", fields...) } } } // shouldSkipHTTPLog 判断是否为无需记录的普通请求。 func shouldSkipHTTPLog(_, _ string, status int, latencyMs float64) bool { if status >= 500 || status == http.StatusTooManyRequests { return false } if latencyMs >= slowRequestThresholdMs { return false } return true } func meaningfulAuthFailure(c *gin.Context) bool { value, ok := c.Get(ContextAuthFailureReason) if !ok { return false } reason, _ := value.(string) return reason != "" && reason != "missing" }