Files
gemini-banlancer/internal/middleware/log.go
2025-11-20 12:24:05 +08:00

85 lines
2.3 KiB
Go

// Filename: internal/middleware/log_redaction.go
package middleware
import (
"bytes"
"io"
"regexp"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
const RedactedBodyKey = "redactedBody"
const RedactedAuthHeaderKey = "redactedAuthHeader"
const RedactedValue = `"[REDACTED]"`
func RedactionMiddleware() gin.HandlerFunc {
// Pre-compile regex for efficiency
jsonKeyPattern := regexp.MustCompile(`("api_key"|"keys")\s*:\s*"[^"]*"`)
bearerTokenPattern := regexp.MustCompile(`^(Bearer\s+)\S+$`)
return func(c *gin.Context) {
// --- 1. Redact Request Body ---
if c.Request.Method == "POST" || c.Request.Method == "PUT" || c.Request.Method == "DELETE" {
if bodyBytes, err := io.ReadAll(c.Request.Body); err == nil {
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
bodyString := string(bodyBytes)
redactedBody := jsonKeyPattern.ReplaceAllString(bodyString, `$1:`+RedactedValue)
c.Set(RedactedBodyKey, redactedBody)
}
}
// --- 2. Redact Authorization Header ---
authHeader := c.GetHeader("Authorization")
if authHeader != "" {
if bearerTokenPattern.MatchString(authHeader) {
redactedHeader := bearerTokenPattern.ReplaceAllString(authHeader, `${1}[REDACTED]`)
c.Set(RedactedAuthHeaderKey, redactedHeader)
}
}
c.Next()
}
}
// LogrusLogger is a Gin middleware that logs requests using a Logrus logger.
// It consumes redacted data prepared by the RedactionMiddleware.
func LogrusLogger(logger *logrus.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
// Process request
c.Next()
// After request, gather data and log
latency := time.Since(start)
statusCode := c.Writer.Status()
entry := logger.WithFields(logrus.Fields{
"status_code": statusCode,
"latency_ms": latency.Milliseconds(),
"client_ip": c.ClientIP(),
"method": c.Request.Method,
"path": path,
})
if redactedBody, exists := c.Get(RedactedBodyKey); exists {
entry = entry.WithField("body", redactedBody)
}
if redactedAuth, exists := c.Get(RedactedAuthHeaderKey); exists {
entry = entry.WithField("authorization", redactedAuth)
}
if len(c.Errors) > 0 {
entry.Error(c.Errors.String())
} else {
entry.Info("request handled")
}
}
}