Skip to content

增加调试日志并记录请求体 - #2731

Closed
tanranv5 wants to merge 3 commits into
QuantumNous:mainfrom
tanranv5:feat/add-debug-logs-requestbody
Closed

增加调试日志并记录请求体#2731
tanranv5 wants to merge 3 commits into
QuantumNous:mainfrom
tanranv5:feat/add-debug-logs-requestbody

Conversation

@tanranv5

@tanranv5 tanranv5 commented Jan 24, 2026

Copy link
Copy Markdown

变更说明

  • 模型映射、请求头覆盖增加调试日志,输出映射链路/覆盖明细(已做敏感值脱敏)
  • 渠道重试流程增加调试日志,记录选渠、重试与失败信息
  • 错误日志 other 字段补充 request_body

测试

  • 未运行

Summary by CodeRabbit

  • New Features

    • Enhanced logging and observability across relay operations, including channel selection, request handling, and retry attempts
    • Implemented sensitive header masking in logs for improved security
  • Bug Fixes

    • Improved request body error handling and recovery during retry operations
    • Enhanced error context tracking with detailed request information and validation

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 24, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR adds extensive observability and validation improvements to the relay system. It introduces comprehensive debug logging throughout relay and task retry paths, adds header value masking for security in logs, refactors header override processing with validation, and implements model mapping chain tracking with detailed logging.

Changes

Cohort / File(s) Summary
Relay Logging & Error Handling
controller/relay.go
Added extensive debug logging around channel selection, request handling, and retry control flows in both Relay and RelayTask paths. Enhanced error handling in retry loops with immediate status code setting on body read failures. Adjusted retry control flow to compute remaining retry count per iteration and log outcomes. Modified processChannelError to capture request body details for error context.
Header Processing & Masking
relay/channel/api_request.go
Introduced three header masking helper functions to mask sensitive values in logs. Refactored processHeaderOverride to accept gin.Context alongside RelayInfo for contextual logging and channel metadata extraction. Added validation for header override values (non-string detection) and support for {api_key} placeholder replacement. Updated all call sites to pass gin.Context.
Model Mapping Observability
relay/helper/model_mapped.go
Added detailed logging for model mapping checks, parsed mapping tables, and final mapping outcomes. Implemented chain tracking mechanism that initializes with the origin model and appends successful redirections. Introduced upstreamModel defaulting behavior to fall back to OriginModelName when UpstreamModelName is empty.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • feat: endpoint type log #2038 — Modifies processChannelError to enrich error logging with additional context (request path in that PR vs. request body details in this one).
  • feats:add custom headers override #1644 — Directly modifies processHeaderOverride and header-override handling in relay/channel/api_request.go, overlapping with this PR's refactoring.
  • task_relay_info #1656 — Refactors RelayTask control flow in controller/relay.go, complementary to this PR's logging and error handling enhancements.

Suggested reviewers

  • creamlike1024
  • xyfacai

Poem

🐰 Observability hops through the relay,
Logs brighten each channel's way,
Headers masked, chains now tracked with care,
Debug insights float everywhere!
From request to response, we see it all—
No hidden error shall fall!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title in Chinese '增加调试日志并记录请求体' translates to 'Add debug logs and record request body', which directly matches the PR objectives of adding debug logging and request body information to error logs across model mapping, header override, and channel retry flows.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
relay/channel/api_request.go (1)

41-106: Mask header overrides by header name, not just API key.

Current masking only replaces occurrences of info.ApiKey. If overrides include Authorization, X-API-Key, Cookie, etc., those values will be logged in cleartext. Consider redacting known sensitive headers regardless of value.

✅ Suggested fix (name-based redaction)
+var sensitiveHeaderKeys = map[string]struct{}{
+    "authorization":      {},
+    "proxy-authorization": {},
+    "x-api-key":          {},
+    "api-key":            {},
+    "cookie":             {},
+    "set-cookie":         {},
+}
+
+func isSensitiveHeaderKey(key string) bool {
+    _, ok := sensitiveHeaderKeys[strings.ToLower(key)]
+    return ok
+}
+
 func maskHeaderOverrideForLog(headerOverride map[string]interface{}, apiKey string) map[string]interface{} {
     if len(headerOverride) == 0 {
         return nil
     }
     masked := make(map[string]interface{}, len(headerOverride))
     for key, value := range headerOverride {
+        if isSensitiveHeaderKey(key) {
+            masked[key] = "***"
+            continue
+        }
         if str, ok := value.(string); ok {
             masked[key] = maskHeaderOverrideValue(str, apiKey)
         } else {
             masked[key] = value
         }
     }
     return masked
 }
 
 func maskHeaderOverrideForLogString(headerOverride map[string]string, apiKey string) map[string]string {
     if len(headerOverride) == 0 {
         return nil
     }
     masked := make(map[string]string, len(headerOverride))
     for key, value := range headerOverride {
+        if isSensitiveHeaderKey(key) {
+            masked[key] = "***"
+            continue
+        }
         masked[key] = maskHeaderOverrideValue(value, apiKey)
     }
     return masked
 }
🤖 Fix all issues with AI agents
In `@controller/relay.go`:
- Around line 361-366: The current error logging stores the full raw request
body from common.GetRequestBody into the telemetry map (other["request_body"])
which may leak sensitive data and bloat logs; update the relay handler to avoid
persisting raw bodies by inspecting the request Content-Type and, for text
types, applying masking or truncation (e.g., limit to N bytes and replace
sensitive fields) before assigning to other["request_body"], and for non-text or
large payloads set a safe placeholder (e.g., "<omitted: binary/large payload>")
or set a flag like other["request_body_omitted"]=true; still record body read
errors to other["request_body_error"] as before.

In `@relay/helper/model_mapped.go`:
- Around line 64-71: The code computes a local upstreamModel but still calls
request.SetModelName(info.UpstreamModelName), which can pass an empty string and
clear the request; change the mutation to use the normalized upstreamModel (or
only call SetModelName when upstreamModel != "") so the request and the logger
remain consistent—update the block around upstreamModel, logger.LogDebug, and
request.SetModelName to use the upstreamModel variable (or guard the
SetModelName call) instead of info.UpstreamModelName.

Comment thread controller/relay.go
Comment on lines +361 to +366
requestBody, bodyErr := common.GetRequestBody(c)
if bodyErr == nil {
other["request_body"] = string(requestBody)
} else {
other["request_body_error"] = bodyErr.Error()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid persisting raw request bodies to error logs.

Storing full request bodies in error logs risks leaking sensitive data and can bloat storage (especially for large or binary payloads). Consider truncation and/or masking, and optionally gating by content type.

✅ Suggested fix (truncate + flag)
 requestBody, bodyErr := common.GetRequestBody(c)
 if bodyErr == nil {
-    other["request_body"] = string(requestBody)
+    const maxLogBytes = 2048
+    bodyForLog := requestBody
+    if len(bodyForLog) > maxLogBytes {
+        bodyForLog = bodyForLog[:maxLogBytes]
+        other["request_body_truncated"] = true
+    }
+    other["request_body"] = string(bodyForLog)
 } else {
     other["request_body_error"] = bodyErr.Error()
 }
🤖 Prompt for AI Agents
In `@controller/relay.go` around lines 361 - 366, The current error logging stores
the full raw request body from common.GetRequestBody into the telemetry map
(other["request_body"]) which may leak sensitive data and bloat logs; update the
relay handler to avoid persisting raw bodies by inspecting the request
Content-Type and, for text types, applying masking or truncation (e.g., limit to
N bytes and replace sensitive fields) before assigning to other["request_body"],
and for non-text or large payloads set a safe placeholder (e.g., "<omitted:
binary/large payload>") or set a flag like other["request_body_omitted"]=true;
still record body read errors to other["request_body_error"] as before.

Comment on lines +64 to 71
upstreamModel := info.UpstreamModelName
if upstreamModel == "" {
upstreamModel = info.OriginModelName
}
logger.LogDebug(c, "模型映射结果: 原模型=%q, 映射链路=%s, 上游模型=%q, 已映射=%t", info.OriginModelName, strings.Join(chain, "->"), upstreamModel, info.IsModelMapped)
if request != nil {
request.SetModelName(info.UpstreamModelName)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Default upstream model before mutating the request.

upstreamModel is defaulted locally, but request.SetModelName(info.UpstreamModelName) can still receive an empty string when mapping is skipped, causing the request model to be cleared and logs to disagree with runtime behavior. Consider normalizing info.UpstreamModelName before use.

✅ Suggested fix
-    upstreamModel := info.UpstreamModelName
-    if upstreamModel == "" {
-        upstreamModel = info.OriginModelName
-    }
+    if info.UpstreamModelName == "" {
+        info.UpstreamModelName = info.OriginModelName
+    }
+    upstreamModel := info.UpstreamModelName
     logger.LogDebug(c, "模型映射结果: 原模型=%q, 映射链路=%s, 上游模型=%q, 已映射=%t", info.OriginModelName, strings.Join(chain, "->"), upstreamModel, info.IsModelMapped)
     if request != nil {
         request.SetModelName(info.UpstreamModelName)
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
upstreamModel := info.UpstreamModelName
if upstreamModel == "" {
upstreamModel = info.OriginModelName
}
logger.LogDebug(c, "模型映射结果: 原模型=%q, 映射链路=%s, 上游模型=%q, 已映射=%t", info.OriginModelName, strings.Join(chain, "->"), upstreamModel, info.IsModelMapped)
if request != nil {
request.SetModelName(info.UpstreamModelName)
}
if info.UpstreamModelName == "" {
info.UpstreamModelName = info.OriginModelName
}
upstreamModel := info.UpstreamModelName
logger.LogDebug(c, "模型映射结果: 原模型=%q, 映射链路=%s, 上游模型=%q, 已映射=%t", info.OriginModelName, strings.Join(chain, "->"), upstreamModel, info.IsModelMapped)
if request != nil {
request.SetModelName(info.UpstreamModelName)
}
🤖 Prompt for AI Agents
In `@relay/helper/model_mapped.go` around lines 64 - 71, The code computes a local
upstreamModel but still calls request.SetModelName(info.UpstreamModelName),
which can pass an empty string and clear the request; change the mutation to use
the normalized upstreamModel (or only call SetModelName when upstreamModel !=
"") so the request and the logger remain consistent—update the block around
upstreamModel, logger.LogDebug, and request.SetModelName to use the
upstreamModel variable (or guard the SetModelName call) instead of
info.UpstreamModelName.

@tanranv5 tanranv5 closed this Jan 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant