backend-service/internal/middleware/logging.go

115 lines
2.7 KiB
Go
Raw Permalink Normal View History

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])
}