package utils import ( "io" "os" "time" "github.com/gin-gonic/gin" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) // InitLogger initializes the zerolog logger func InitLogger(debug bool) { zerolog.TimeFieldFormat = time.RFC3339 var output io.Writer = os.Stdout if debug { // Pretty console output for development output = zerolog.ConsoleWriter{ Out: os.Stdout, TimeFormat: "15:04:05", } zerolog.SetGlobalLevel(zerolog.DebugLevel) } else { // JSON output for production zerolog.SetGlobalLevel(zerolog.InfoLevel) } log.Logger = zerolog.New(output).With().Timestamp().Caller().Logger() } // GinLogger returns a Gin middleware for request logging func GinLogger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path raw := c.Request.URL.RawQuery // Process request c.Next() // Log after request end := time.Now() latency := end.Sub(start) if raw != "" { path = path + "?" + raw } msg := "Request" if len(c.Errors) > 0 { msg = c.Errors.String() } event := log.Info() statusCode := c.Writer.Status() if statusCode >= 400 && statusCode < 500 { event = log.Warn() } else if statusCode >= 500 { event = log.Error() } event. Str("method", c.Request.Method). Str("path", path). Int("status", statusCode). Str("ip", c.ClientIP()). Dur("latency", latency). Str("user-agent", c.Request.UserAgent()). Msg(msg) } } // GinRecovery returns a Gin middleware for panic recovery func GinRecovery() gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { log.Error(). Interface("error", err). Str("path", c.Request.URL.Path). Str("method", c.Request.Method). Msg("Panic recovered") c.AbortWithStatusJSON(500, gin.H{ "error": "Internal server error", }) } }() c.Next() } }