package middleware import ( "log" "net/url" "strings" "time" "github.com/gin-gonic/gin" ) var sensitiveQueryKeys = map[string]struct{}{ "token": {}, "access_token": {}, "refresh_token": {}, "code": {}, "secret": {}, } func Logger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path raw := sanitizeQuery(c.Request.URL.RawQuery) c.Next() latency := time.Since(start) status := c.Writer.Status() method := c.Request.Method ip := c.ClientIP() userAgent := c.Request.UserAgent() userID, _ := c.Get("user_id") log.Printf("[API] %s %s %s | status: %d | latency: %v | ip: %s | user_id: %v | ua: %s", time.Now().Format("2006-01-02 15:04:05"), method, path, status, latency, ip, userID, userAgent, ) if len(c.Errors) > 0 { for _, err := range c.Errors { log.Printf("[Error] %v", err) } } if raw != "" { log.Printf("[Query] %s?%s", path, raw) } } } func sanitizeQuery(raw string) string { if raw == "" { return "" } values, err := url.ParseQuery(raw) if err != nil { return "" } for key := range values { if isSensitiveQueryKey(key) { values.Set(key, "***") } } return values.Encode() } func isSensitiveQueryKey(key string) bool { normalized := strings.ToLower(strings.TrimSpace(key)) if _, ok := sensitiveQueryKeys[normalized]; ok { return true } return strings.Contains(normalized, "token") || strings.Contains(normalized, "secret") }