feat: log non-200/non-429 kiro errors to rolling file for debugging
Some checks failed
Build Docker Image / build (push) Has been cancelled

Writes request body + response body of failed upstream calls to
kiro_errors.log in the working directory. File is capped at 10MB;
when the next write would exceed that, the file is truncated so
only the most recent records are kept.

Helps diagnose 400 "Improperly formed request" errors where both
CW and Q reject the same payload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 11:33:27 +08:00
parent e8ab5b11e7
commit 64df2d6083
2 changed files with 52 additions and 0 deletions

47
proxy/error_log.go Normal file
View File

@@ -0,0 +1,47 @@
package proxy
import (
"fmt"
"os"
"sync"
"time"
)
const (
kiroErrorLogPath = "kiro_errors.log"
kiroErrorLogMaxSize = 10 * 1024 * 1024 // 10MB, truncate and restart when exceeded
)
var kiroErrorLogMu sync.Mutex
// logKiroError appends a non-200/non-429 failure record to kiro_errors.log in the
// current working directory. The file is capped at 10MB; when the next write would
// push it over the limit, the file is truncated so only the most recent records
// are retained.
func logKiroError(reqID, endpoint string, status int, account, model string, reqBody, respBody []byte) {
kiroErrorLogMu.Lock()
defer kiroErrorLogMu.Unlock()
entry := fmt.Sprintf(
"===== %s req=%s endpoint=%s status=%d account=%s model=%s =====\n"+
"-- request body --\n%s\n"+
"-- response body --\n%s\n\n",
time.Now().Format("2006-01-02 15:04:05"),
reqID, endpoint, status, account, model,
string(reqBody), string(respBody),
)
// If the next entry would exceed the cap, truncate the file first.
if info, err := os.Stat(kiroErrorLogPath); err == nil {
if info.Size()+int64(len(entry)) > kiroErrorLogMaxSize {
_ = os.Truncate(kiroErrorLogPath, 0)
}
}
f, err := os.OpenFile(kiroErrorLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return
}
defer f.Close()
_, _ = f.WriteString(entry)
}

View File

@@ -285,6 +285,11 @@ func CallKiroAPI(account *config.Account, payload *KiroPayload, callback *KiroSt
lastStatus = fmt.Sprintf("%d", resp.StatusCode)
bodyStr := string(errBody)
// 记录非 200 / 非 429 的请求体和响应体以便排查(本地滚动日志,上限 10MB
if resp.StatusCode != 429 {
logKiroError(reqID, ep.Name, resp.StatusCode, accountLabel, modelID, reqBody, errBody)
}
if resp.StatusCode == 401 || resp.StatusCode == 403 {
log.Printf("[KiroAPI] %d %s %s/a%d auth_error %s %s", resp.StatusCode, reqID, epShort, attempt, fmtMs(time.Since(attemptStart)), truncateForLog(bodyStr, 200))
log.Printf("[KiroAPI] FAIL %s all endpoints failed %s last=%s", reqID, fmtMs(time.Since(requestStart)), lastStatus)