98 lines
2.0 KiB
Go
98 lines
2.0 KiB
Go
|
|
package middleware
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"log"
|
||
|
|
"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 {
|
||
|
|
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)
|
||
|
|
|
||
|
|
// Get client IP
|
||
|
|
clientIP := c.ClientIP()
|
||
|
|
|
||
|
|
// Get method
|
||
|
|
method := c.Request.Method
|
||
|
|
|
||
|
|
// Get status code
|
||
|
|
statusCode := c.Writer.Status()
|
||
|
|
|
||
|
|
// Get body size
|
||
|
|
bodySize := c.Writer.Size()
|
||
|
|
|
||
|
|
// Build full path with query string
|
||
|
|
if raw != "" {
|
||
|
|
path = path + "?" + raw
|
||
|
|
}
|
||
|
|
|
||
|
|
// Log structured information
|
||
|
|
log.Printf("REQUEST: method=%s path=%s status=%d latency=%v client_ip=%s body_size=%d user_agent=\"%s\"",
|
||
|
|
method,
|
||
|
|
path,
|
||
|
|
statusCode,
|
||
|
|
latency,
|
||
|
|
clientIP,
|
||
|
|
bodySize,
|
||
|
|
c.Request.UserAgent(),
|
||
|
|
)
|
||
|
|
|
||
|
|
// Log errors if any
|
||
|
|
if len(c.Errors) > 0 {
|
||
|
|
log.Printf("ERRORS: %v", c.Errors.String())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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])
|
||
|
|
}
|