package middleware import ( "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" ) func RequestID() gin.HandlerFunc { return func(c *gin.Context) { traceID := c.GetHeader("X-Request-ID") if traceID == "" { traceID = uuid.New().String() } c.Set("trace_id", traceID) c.Header("X-Request-ID", traceID) c.Next() } } func RequestLogger(log *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery c.Next() latency := time.Since(start) status := c.Writer.Status() fields := []zap.Field{ zap.Int("status", status), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.String("ip", c.ClientIP()), zap.String("user-agent", c.Request.UserAgent()), zap.Duration("latency", latency), zap.String("trace_id", c.GetString("trace_id")), } if c.Writer.Status() >= 500 { log.Error("request completed with server error", fields...) } else if c.Writer.Status() >= 400 { log.Warn("request completed with client error", fields...) } else { log.Info("request", fields...) } } } func Recovery(log *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if r := recover(); r != nil { log.Error("panic recovered", zap.Any("error", r), zap.String("path", c.Request.URL.Path), zap.String("trace_id", c.GetString("trace_id")), ) c.AbortWithStatusJSON(500, gin.H{ "error": "INTERNAL_ERROR", "message": "internal server error", }) } }() c.Next() } }