package middleware import ( "bytes" "ckwk/internal/conf" "io" "net/http" "strings" "time" "ckwk/pkg/log" "github.com/gin-gonic/gin" "go.uber.org/zap/zapcore" ) const maxDebugBodySize = 4 * 1024 type debugBodyWriter struct { gin.ResponseWriter body bytes.Buffer } func (w *debugBodyWriter) Write(data []byte) (int, error) { w.body.Write(data) return w.ResponseWriter.Write(data) } func (w *debugBodyWriter) WriteString(data string) (int, error) { w.body.WriteString(data) return w.ResponseWriter.WriteString(data) } func DebugHTTPLog() gin.HandlerFunc { return func(ctx *gin.Context) { if !conf.IsRuntimeDebugEnabled() { ctx.Next() return } if !shouldCaptureBackendRoute(ctx.Request.URL.Path) { ctx.Next() return } startAt := time.Now() requestBody := readRequestBody(ctx.Request) writer := &debugBodyWriter{ResponseWriter: ctx.Writer} ctx.Writer = writer ctx.Next() fields := map[string]any{ "method": ctx.Request.Method, "path": ctx.Request.URL.Path, "rawQuery": ctx.Request.URL.RawQuery, "status": writer.Status(), "durationMs": time.Since(startAt).Milliseconds(), "clientIP": ctx.ClientIP(), "requestHeader": log.SanitizeHeaders(ctx.Request.Header), "requestBody": truncate(log.SanitizeBody(ctx.ContentType(), requestBody), maxDebugBodySize), "responseHeader": log.SanitizeHeaders(http.Header(writer.Header().Clone())), "responseBody": truncate(log.SanitizeBody(writer.Header().Get("Content-Type"), writer.body.String()), maxDebugBodySize), "responseSize": writer.Size(), "handler": ctx.HandlerName(), "abortWithErrors": ctx.Errors.ByType(gin.ErrorTypeAny).String(), } log.Capture(zapcore.DebugLevel, "http", "incoming exchange", fields) } } func readRequestBody(r *http.Request) string { if r == nil || r.Body == nil { return "" } body, err := io.ReadAll(r.Body) if err != nil { return "" } r.Body = io.NopCloser(bytes.NewBuffer(body)) return string(body) } func truncate(value string, limit int) string { if len(value) <= limit { return value } return value[:limit] + "...(truncated)" } func isDebugLogRoute(path string) bool { return strings.HasPrefix(path, "/api/debug/") } func shouldCaptureBackendRoute(path string) bool { if !strings.HasPrefix(path, "/api/") { return false } return !isDebugLogRoute(path) }