veza/veza-backend-api/internal/middleware/recovery.go
2025-12-16 11:23:49 -05:00

64 lines
1.6 KiB
Go

package middleware
import (
"net/http"
"runtime/debug"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// Recovery middleware personnalisé avec logging structuré
// Capture les panics et les log avec stack trace et contexte
// MOD-P1-005: Stack traces seulement si includeStackTrace=true (dev/DEBUG mode)
func Recovery(logger *zap.Logger, includeStackTrace bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
requestID, _ := c.Get("request_id")
// MOD-P1-005: Stack traces seulement en development/debug, pas en prod
var stack []byte
if includeStackTrace {
stack = debug.Stack()
}
// Construire les champs de log
logFields := []zap.Field{
zap.Any("error", err),
zap.String("path", c.Request.URL.Path),
zap.String("method", c.Request.Method),
}
if len(stack) > 0 {
logFields = append(logFields, zap.ByteString("stack", stack))
}
// Ajouter request_id si disponible
if requestID != nil {
if requestIDStr, ok := requestID.(string); ok {
logFields = append(logFields, zap.String("request_id", requestIDStr))
}
}
// Ajouter user_id si disponible
if userID, exists := c.Get("user_id"); exists {
logFields = append(logFields, zap.Any("user_id", userID))
}
logger.Error("Panic recovered", logFields...)
// Retourner une erreur 500 standardisée
c.JSON(http.StatusInternalServerError, gin.H{
"error": gin.H{
"code": 9000,
"message": "Internal server error",
},
})
c.Abort()
}
}()
c.Next()
}
}