package middleware import ( "bytes" "context" "io" "strings" "time" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // ComprehensiveLoggerMiddleware 全面日志中间件 type ComprehensiveLoggerMiddleware struct { logger *zap.Logger config *ComprehensiveLoggerConfig } // ComprehensiveLoggerConfig 全面日志配置 type ComprehensiveLoggerConfig struct { EnableRequestLogging bool // 是否记录请求日志 EnableResponseLogging bool // 是否记录响应日志 EnableRequestBodyLogging bool // 是否记录请求体 EnableErrorLogging bool // 是否记录错误日志 EnableBusinessLogging bool // 是否记录业务日志 EnablePerformanceLogging bool // 是否记录性能日志 MaxBodySize int64 // 最大记录体大小 ExcludePaths []string // 排除的路径 IncludePaths []string // 包含的路径 } // NewComprehensiveLoggerMiddleware 创建全面日志中间件 func NewComprehensiveLoggerMiddleware(logger *zap.Logger, config *ComprehensiveLoggerConfig) *ComprehensiveLoggerMiddleware { if config == nil { config = &ComprehensiveLoggerConfig{ EnableRequestLogging: true, EnableResponseLogging: true, EnableRequestBodyLogging: false, // 生产环境默认关闭 EnableErrorLogging: true, EnableBusinessLogging: true, EnablePerformanceLogging: true, MaxBodySize: 1024 * 10, // 10KB ExcludePaths: []string{"/health", "/metrics", "/favicon.ico"}, } } return &ComprehensiveLoggerMiddleware{ logger: logger, config: config, } } // GetName 返回中间件名称 func (m *ComprehensiveLoggerMiddleware) GetName() string { return "comprehensive_logger" } // GetPriority 返回中间件优先级 func (m *ComprehensiveLoggerMiddleware) GetPriority() int { return 90 // 高优先级,在panic恢复之后 } // Handle 返回中间件处理函数 func (m *ComprehensiveLoggerMiddleware) Handle() gin.HandlerFunc { return func(c *gin.Context) { // 检查是否应该记录此路径 if !m.shouldLogPath(c.Request.URL.Path) { c.Next() return } startTime := time.Now() requestID := c.GetString("request_id") traceID := c.GetString("trace_id") userID := c.GetString("user_id") // 记录请求开始 if m.config.EnableRequestLogging { m.logRequest(c, startTime, requestID, traceID, userID) } // 捕获请求体(如果需要) var requestBody []byte if m.config.EnableRequestBodyLogging && m.shouldLogRequestBody(c) { requestBody = m.captureRequestBody(c) } // 创建响应写入器包装器 responseWriter := &responseWriterWrapper{ ResponseWriter: c.Writer, logger: m.logger, config: m.config, requestID: requestID, traceID: traceID, userID: userID, startTime: startTime, path: c.Request.URL.Path, method: c.Request.Method, } c.Writer = responseWriter // 处理请求 c.Next() // 记录响应 if m.config.EnableResponseLogging { m.logResponse(c, responseWriter, startTime, requestID, traceID, userID, requestBody) } // 记录错误 if m.config.EnableErrorLogging && len(c.Errors) > 0 { m.logErrors(c, requestID, traceID, userID) } // 记录性能指标 if m.config.EnablePerformanceLogging { m.logPerformance(c, startTime, requestID, traceID, userID) } } } // IsGlobal 是否为全局中间件 func (m *ComprehensiveLoggerMiddleware) IsGlobal() bool { return true } // shouldLogPath 检查是否应该记录此路径 func (m *ComprehensiveLoggerMiddleware) shouldLogPath(path string) bool { // 检查排除路径 for _, excludePath := range m.config.ExcludePaths { if strings.HasPrefix(path, excludePath) { return false } } // 检查包含路径(如果指定了) if len(m.config.IncludePaths) > 0 { for _, includePath := range m.config.IncludePaths { if strings.HasPrefix(path, includePath) { return true } } return false } return true } // shouldLogRequestBody 检查是否应该记录请求体 func (m *ComprehensiveLoggerMiddleware) shouldLogRequestBody(c *gin.Context) bool { contentType := c.GetHeader("Content-Type") return strings.Contains(contentType, "application/json") || strings.Contains(contentType, "application/x-www-form-urlencoded") } // captureRequestBody 捕获请求体 func (m *ComprehensiveLoggerMiddleware) captureRequestBody(c *gin.Context) []byte { if c.Request.Body == nil { return nil } body, err := io.ReadAll(c.Request.Body) if err != nil { return nil } // 重新设置body c.Request.Body = io.NopCloser(bytes.NewBuffer(body)) // 限制大小 if int64(len(body)) > m.config.MaxBodySize { return body[:m.config.MaxBodySize] } return body } // logRequest 记录请求日志 func (m *ComprehensiveLoggerMiddleware) logRequest(c *gin.Context, startTime time.Time, requestID, traceID, userID string) { logFields := []zap.Field{ zap.String("log_type", "request"), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.String("user_id", userID), zap.String("method", c.Request.Method), zap.String("path", c.Request.URL.Path), zap.String("query", c.Request.URL.RawQuery), zap.String("client_ip", c.ClientIP()), zap.String("user_agent", c.Request.UserAgent()), zap.String("referer", c.Request.Referer()), zap.Int64("content_length", c.Request.ContentLength), zap.String("content_type", c.GetHeader("Content-Type")), zap.Time("timestamp", startTime), } m.logger.Info("收到HTTP请求", logFields...) } // logResponse 记录响应日志 func (m *ComprehensiveLoggerMiddleware) logResponse(c *gin.Context, responseWriter *responseWriterWrapper, startTime time.Time, requestID, traceID, userID string, requestBody []byte) { duration := time.Since(startTime) statusCode := responseWriter.Status() logFields := []zap.Field{ zap.String("log_type", "response"), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.String("user_id", userID), zap.String("method", c.Request.Method), zap.String("path", c.Request.URL.Path), zap.Int("status_code", statusCode), zap.Duration("duration", duration), zap.Int("response_size", responseWriter.Size()), zap.Time("timestamp", time.Now()), } // 添加请求体(如果记录了) if len(requestBody) > 0 { logFields = append(logFields, zap.String("request_body", string(requestBody))) } // 根据状态码选择日志级别 if statusCode >= 500 { m.logger.Error("HTTP响应错误", logFields...) } else if statusCode >= 400 { m.logger.Warn("HTTP响应警告", logFields...) } else { m.logger.Info("HTTP响应成功", logFields...) } } // logErrors 记录错误日志 func (m *ComprehensiveLoggerMiddleware) logErrors(c *gin.Context, requestID, traceID, userID string) { for _, ginErr := range c.Errors { logFields := []zap.Field{ zap.String("log_type", "error"), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.String("user_id", userID), zap.String("method", c.Request.Method), zap.String("path", c.Request.URL.Path), zap.String("error_type", string(ginErr.Type)), zap.Error(ginErr.Err), zap.Time("timestamp", time.Now()), } m.logger.Error("请求处理错误", logFields...) } } // logPerformance 记录性能日志 func (m *ComprehensiveLoggerMiddleware) logPerformance(c *gin.Context, startTime time.Time, requestID, traceID, userID string) { duration := time.Since(startTime) // 记录慢请求 if duration > 1*time.Second { logFields := []zap.Field{ zap.String("log_type", "performance"), zap.String("performance_type", "slow_request"), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.String("user_id", userID), zap.String("method", c.Request.Method), zap.String("path", c.Request.URL.Path), zap.Duration("duration", duration), zap.Time("timestamp", time.Now()), } m.logger.Warn("检测到慢请求", logFields...) } // 记录性能指标 logFields := []zap.Field{ zap.String("log_type", "performance"), zap.String("performance_type", "request_metrics"), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.String("user_id", userID), zap.String("method", c.Request.Method), zap.String("path", c.Request.URL.Path), zap.Duration("duration", duration), zap.Time("timestamp", time.Now()), } m.logger.Debug("请求性能指标", logFields...) } // responseWriterWrapper 响应写入器包装器 type responseWriterWrapper struct { gin.ResponseWriter logger *zap.Logger config *ComprehensiveLoggerConfig requestID string traceID string userID string startTime time.Time path string method string status int size int } // Write 实现Write方法 func (w *responseWriterWrapper) Write(b []byte) (int, error) { size, err := w.ResponseWriter.Write(b) w.size += size return size, err } // WriteHeader 实现WriteHeader方法 func (w *responseWriterWrapper) WriteHeader(code int) { w.status = code w.ResponseWriter.WriteHeader(code) } // WriteString 实现WriteString方法 func (w *responseWriterWrapper) WriteString(s string) (int, error) { size, err := w.ResponseWriter.WriteString(s) w.size += size return size, err } // Status 获取状态码 func (w *responseWriterWrapper) Status() int { return w.status } // Size 获取响应大小 func (w *responseWriterWrapper) Size() int { return w.size } // BusinessLogger 业务日志记录器 type BusinessLogger struct { logger *zap.Logger } // NewBusinessLogger 创建业务日志记录器 func NewBusinessLogger(logger *zap.Logger) *BusinessLogger { return &BusinessLogger{ logger: logger, } } // LogUserAction 记录用户操作 func (bl *BusinessLogger) LogUserAction(ctx context.Context, action string, details map[string]interface{}) { requestID := bl.getRequestIDFromContext(ctx) traceID := bl.getTraceIDFromContext(ctx) userID := bl.getUserIDFromContext(ctx) logFields := []zap.Field{ zap.String("log_type", "business"), zap.String("business_type", "user_action"), zap.String("action", action), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.String("user_id", userID), zap.Time("timestamp", time.Now()), } // 添加详细信息 for key, value := range details { logFields = append(logFields, zap.Any(key, value)) } bl.logger.Info("用户操作记录", logFields...) } // LogBusinessEvent 记录业务事件 func (bl *BusinessLogger) LogBusinessEvent(ctx context.Context, event string, details map[string]interface{}) { requestID := bl.getRequestIDFromContext(ctx) traceID := bl.getTraceIDFromContext(ctx) userID := bl.getUserIDFromContext(ctx) logFields := []zap.Field{ zap.String("log_type", "business"), zap.String("business_type", "business_event"), zap.String("event", event), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.String("user_id", userID), zap.Time("timestamp", time.Now()), } // 添加详细信息 for key, value := range details { logFields = append(logFields, zap.Any(key, value)) } bl.logger.Info("业务事件记录", logFields...) } // LogSystemEvent 记录系统事件 func (bl *BusinessLogger) LogSystemEvent(ctx context.Context, event string, details map[string]interface{}) { requestID := bl.getRequestIDFromContext(ctx) traceID := bl.getTraceIDFromContext(ctx) logFields := []zap.Field{ zap.String("log_type", "business"), zap.String("business_type", "system_event"), zap.String("event", event), zap.String("request_id", requestID), zap.String("trace_id", traceID), zap.Time("timestamp", time.Now()), } // 添加详细信息 for key, value := range details { logFields = append(logFields, zap.Any(key, value)) } bl.logger.Info("系统事件记录", logFields...) } // 辅助方法 func (bl *BusinessLogger) getRequestIDFromContext(ctx context.Context) string { if requestID := ctx.Value("request_id"); requestID != nil { if id, ok := requestID.(string); ok { return id } } return "" } func (bl *BusinessLogger) getTraceIDFromContext(ctx context.Context) string { if traceID := ctx.Value("trace_id"); traceID != nil { if id, ok := traceID.(string); ok { return id } } return "" } func (bl *BusinessLogger) getUserIDFromContext(ctx context.Context) string { if userID := ctx.Value("user_id"); userID != nil { if id, ok := userID.(string); ok { return id } } return "" }