package middleware import ( "attune-heart-therapy/internal/logger" "fmt" "time" "github.com/gin-gonic/gin" ) // LoggingMiddleware creates a custom logging middleware with detailed request information func LoggingMiddleware() gin.HandlerFunc { return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { // Custom log format with more details return fmt.Sprintf("[%s] %s %s %s %d %s \"%s\" %s \"%s\" %s\n", param.TimeStamp.Format("2006-01-02 15:04:05"), param.ClientIP, param.Method, param.Path, param.StatusCode, param.Latency, param.Request.UserAgent(), param.ErrorMessage, param.Request.Referer(), formatBodySize(param.BodySize), ) }) } // StructuredLoggingMiddleware creates a structured logging middleware for better log parsing func StructuredLoggingMiddleware() gin.HandlerFunc { log := logger.New("http") return func(c *gin.Context) { // Start timer start := time.Now() path := c.Request.URL.Path raw := c.Request.URL.RawQuery // Process request c.Next() // Calculate latency latency := time.Since(start) // Build full path with query string if raw != "" { path = path + "?" + raw } // Prepare log fields fields := map[string]interface{}{ "method": c.Request.Method, "path": path, "status": c.Writer.Status(), "latency_ms": latency.Milliseconds(), "client_ip": c.ClientIP(), "body_size": c.Writer.Size(), "user_agent": c.Request.UserAgent(), } // Add trace ID if available if traceID, exists := c.Get("trace_id"); exists { fields["trace_id"] = traceID } // Add user ID if available if userID, exists := c.Get("user_id"); exists { fields["user_id"] = userID } // Log based on status code statusCode := c.Writer.Status() if statusCode >= 500 { log.Error("HTTP request completed with server error", nil, fields) } else if statusCode >= 400 { log.Warn("HTTP request completed with client error", fields) } else { log.Info("HTTP request completed successfully", fields) } // Log errors if any if len(c.Errors) > 0 { errorFields := map[string]interface{}{ "method": c.Request.Method, "path": path, "client_ip": c.ClientIP(), "errors": c.Errors.String(), } log.Error("Request completed with errors", c.Errors.Last().Err, errorFields) } } } // formatBodySize formats body size in human readable format func formatBodySize(size int) string { if size == 0 { return "0B" } const unit = 1024 if size < unit { return fmt.Sprintf("%dB", size) } div, exp := int64(unit), 0 for n := size / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.1f%cB", float64(size)/float64(div), "KMGTPE"[exp]) }