mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
107 lines
2.9 KiB
Go
107 lines
2.9 KiB
Go
// Package logging provides Gin middleware for HTTP request logging and panic recovery.
|
|
// It integrates Gin web framework with logrus for structured logging of HTTP requests,
|
|
// responses, and error handling with panic recovery capabilities.
|
|
package logging
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"runtime/debug"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const skipGinLogKey = "__gin_skip_request_logging__"
|
|
|
|
// GinLogrusLogger returns a Gin middleware handler that logs HTTP requests and responses
|
|
// using logrus. It captures request details including method, path, status code, latency,
|
|
// client IP, and any error messages, formatting them in a Gin-style log format.
|
|
//
|
|
// Returns:
|
|
// - gin.HandlerFunc: A middleware handler for request logging
|
|
func GinLogrusLogger() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
start := time.Now()
|
|
path := c.Request.URL.Path
|
|
raw := util.MaskSensitiveQuery(c.Request.URL.RawQuery)
|
|
|
|
c.Next()
|
|
|
|
if shouldSkipGinRequestLogging(c) {
|
|
return
|
|
}
|
|
|
|
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 handler that recovers from panics and logs
|
|
// them using logrus. When a panic occurs, it captures the panic value, stack trace,
|
|
// and request path, then returns a 500 Internal Server Error response to the client.
|
|
//
|
|
// Returns:
|
|
// - gin.HandlerFunc: A middleware handler for panic recovery
|
|
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)
|
|
})
|
|
}
|
|
|
|
// SkipGinRequestLogging marks the provided Gin context so that GinLogrusLogger
|
|
// will skip emitting a log line for the associated request.
|
|
func SkipGinRequestLogging(c *gin.Context) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
c.Set(skipGinLogKey, true)
|
|
}
|
|
|
|
func shouldSkipGinRequestLogging(c *gin.Context) bool {
|
|
if c == nil {
|
|
return false
|
|
}
|
|
val, exists := c.Get(skipGinLogKey)
|
|
if !exists {
|
|
return false
|
|
}
|
|
flag, ok := val.(bool)
|
|
return ok && flag
|
|
}
|