package logging import ( "fmt" "net/http" "runtime/debug" "time" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" ) // GinLogrusLogger writes Gin-style access logs through logrus. func GinLogrusLogger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path raw := c.Request.URL.RawQuery c.Next() if raw != "" { path = path + "?" + raw } latency := time.Since(start) if latency > time.Minute { latency = latency.Truncate(time.Second) } else { latency = latency.Truncate(time.Millisecond) } statusCode := c.Writer.Status() clientIP := c.ClientIP() method := c.Request.Method errorMessage := c.Errors.ByType(gin.ErrorTypePrivate).String() timestamp := time.Now().Format("2006/01/02 - 15:04:05") logLine := fmt.Sprintf("[GIN] %s | %3d | %13v | %15s | %-7s \"%s\"", timestamp, statusCode, latency, clientIP, method, path) if errorMessage != "" { logLine = logLine + " | " + errorMessage } switch { case statusCode >= http.StatusInternalServerError: log.Error(logLine) case statusCode >= http.StatusBadRequest: log.Warn(logLine) default: log.Info(logLine) } } } // GinLogrusRecovery returns a Gin middleware that recovers from panics and logs them via logrus. func GinLogrusRecovery() gin.HandlerFunc { return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) { log.WithFields(log.Fields{ "panic": recovered, "stack": string(debug.Stack()), "path": c.Request.URL.Path, }).Error("recovered from panic") c.AbortWithStatus(http.StatusInternalServerError) }) }