请求明细支持查看原始请求/响应:OT_PROXY_LOG_RAW 开关控制,仅管理员记录与可见,流式全量捕获

- 配置: ProxyConfig.LogRaw (OT_PROXY_LOG_RAW, 默认 false)
- 存储: usage_logs 新增 raw_request/raw_response 文本列 (AutoMigrate)
- 网关: NewGateway 接收 logRaw 参数
- handlers: 三个协议入口按 开关+管理员 条件记录原始请求体
- passthrough: 非流式 copyAndCapture 捕获响应, 流式 streamCopy 累积全部原始 SSE 行, finishUsage 统一写入
- admin API: AdminUsage 返回 raw_request/raw_response (仅管理员)
- 前端: 用量页新增查看入口, 弹窗 tab 切换请求/响应
- gitignore: 修正 server/web/ 忽略规则(尾随空格导致未生效)
This commit is contained in:
Sakurasan
2026-08-20 00:37:48 +08:00
parent 0939f98fb5
commit 10f51cbdae
11 changed files with 111 additions and 4 deletions
+29
View File
@@ -272,6 +272,9 @@ func (g *Gateway) copyAndCapture(c *gin.Context, ch *store.Channel, r io.Reader,
}
}
_, _ = c.Writer.Write(out)
if _, ok := c.Get("raw_request"); ok {
c.Set("raw_response", string(data)) // 上游原始响应(未转换)
}
g.finishUsage(c, ch, start, store.UsageStatusSuccess, "")
}
@@ -283,10 +286,24 @@ func (g *Gateway) streamCopy(c *gin.Context, ch *store.Channel, r io.Reader, sta
flusher = nopFlusher{}
}
// 原始响应捕获:仅管理员且开关开启(raw_request 已 set)时累积上游原始行
_, capture := c.Get("raw_request")
var rawResp strings.Builder
// commitRaw 在记账前把已累积的原始响应写入 context
commitRaw := func() {
if capture {
c.Set("raw_response", rawResp.String())
}
}
scanner := newSSEScanner(r)
for {
line, err := scanner.Next()
if line != nil {
if capture {
rawResp.Write(line)
}
out := line
if lineConv != nil {
out = lineConv(line)
@@ -294,6 +311,7 @@ func (g *Gateway) streamCopy(c *gin.Context, ch *store.Channel, r io.Reader, sta
if out != nil {
if _, werr := w.Write(out); werr != nil {
// 客户端意外断开:按已生成部分收费(canceled)
commitRaw()
g.finishUsage(c, ch, start, store.UsageStatusCanceled, "client_disconnect")
return
}
@@ -307,6 +325,7 @@ func (g *Gateway) streamCopy(c *gin.Context, ch *store.Channel, r io.Reader, sta
}
}
if err != nil {
commitRaw()
if err == io.EOF {
g.finishUsage(c, ch, start, store.UsageStatusSuccess, "")
} else if c.Request.Context().Err() != nil {
@@ -578,6 +597,14 @@ func (g *Gateway) finishUsage(c *gin.Context, ch *store.Channel, start time.Time
chID = ch.ID
}
var rawReq, rawResp string
if v, ok := c.Get("raw_request"); ok {
rawReq, _ = v.(string)
}
if v, ok := c.Get("raw_response"); ok {
rawResp, _ = v.(string)
}
// 密钥今日 token 用量累计(配额检查用)
if g.lim != nil && kidVal > 0 {
g.lim.AddTokens(kidVal, in+out)
@@ -603,6 +630,8 @@ func (g *Gateway) finishUsage(c *gin.Context, ch *store.Channel, start time.Time
LatencyMS: latency,
Status: status,
ErrorCode: errCodePtr,
RawRequest: rawReq,
RawResponse: rawResp,
CreatedAt: time.Now().UTC(),
})
}