package middleware import ( "bytes" "encoding/json" "strings" "github.com/gin-gonic/gin" ) // The global access logger is the single request/response capture point. // OperationAudit consumes these values after the handler returns, avoiding a // second body read/writer wrapper. const ( ctxReqBodyKey = "kra_req_body" ctxRespBufferKey = "kra_resp_buffer" ctxRespTruncatedKey = "kra_resp_truncated" ctxRespTextKey = "kra_resp_text" ) type captureWriter struct { gin.ResponseWriter body bytes.Buffer maxBytes int truncated bool } func (w *captureWriter) Write(data []byte) (int, error) { limit := w.maxBytes if limit <= 0 { limit = 1024 } if w.body.Len() < limit { remaining := limit - w.body.Len() if len(data) > remaining { _, _ = w.body.Write(data[:remaining]) w.truncated = true } else { _, _ = w.body.Write(data) } } else if len(data) > 0 { w.truncated = true } return w.ResponseWriter.Write(data) } func (w *captureWriter) Truncated() bool { return w != nil && w.truncated } func redactJSON(raw []byte, contentType string, limit int) string { if len(raw) == 0 { return "" } if limit <= 0 { limit = 1024 } text := string(raw) if !strings.Contains(strings.ToLower(contentType), "json") { if len(text) > limit { return "[超出记录长度]" } return text } var value any if json.Unmarshal(raw, &value) != nil { if len(text) > limit { return "[超出记录长度]" } return text } maskOperationBody(value) encoded, _ := json.Marshal(value) if len(encoded) > limit { return "[超出记录长度]" } return string(encoded) } func stringValue(value any) string { text, _ := value.(string); return text }