Proxy Monitor:請求日誌、篩選、詳情還原與匯出
你已經把本地反代跑起來了,但一旦出現 401/429/500、流式中斷、或者「怎麼突然換了帳號/換了模型」,排障就很容易變成盲猜。
這節課只講一件事:用 Proxy Monitor 把每次呼叫還原成「可覆盤的證據」,讓你知道請求從哪來、打到哪個端點、用了哪個帳號、模型有沒有被映射、以及 Token 消耗大概是多少。
學完你能做什麼
- 在
/monitor頁面打開/暫停錄製,並理解它會不會影響 Token Stats - 用搜尋框、快速篩選、帳號篩選,快速定位一條請求記錄
- 在詳情彈窗裡查看並複製 Request/Response 報文,覆盤失敗原因
- 知道 Proxy Monitor 的資料落盤位置(
proxy_logs.db)與清空行為 - 了解目前版本「匯出日誌」的真實能力邊界(GUI vs 後端指令)
你現在的困境
- 你只看到「呼叫失敗/逾時」,但不知道到底失敗在上游、代理、還是客戶端設定
- 你懷疑觸發了模型映射或帳號輪替,但沒有證據鏈
- 你想覆盤一條請求的完整 payload(尤其是流式/Thinking),但日誌裡看不到
什麼時候用這一招
- 你要排查:401 鑑權失敗、429 限流、5xx 上游錯誤、流式中斷
- 你想確認:某次請求到底用了哪個帳號(
X-Account-Email) - 你在做模型路由策略,想驗證「客戶端模型名 -> 實際映射模型名」
🎒 開始前的準備
前置條件
Proxy Monitor 記錄的是「反代服務收到的請求」。所以你至少要先跑通:
- 反代服務已啟動,並能存取
/healthz - 你知道目前反代的 Base URL 和連接埠
如果還沒跑通,先看 啟動本地反代並接入第一個客戶端。
什麼是 Proxy Monitor?
Proxy Monitor 是 Antigravity Tools 內建的「請求日誌看板」。每次請求進到本地反代後,它會記錄時間、路徑、狀態碼、耗時、模型與協定,並盡量從回應裡提取 Token 用量;你還能點開單條記錄查看請求與回應報文,用它來覆盤失敗原因和路由/帳號選擇結果。
資料落盤位置
Proxy Monitor 的日誌會寫入資料目錄下的 SQLite:proxy_logs.db。資料目錄怎麼找、怎麼備份,建議先複習 首次啟動必懂:資料目錄、日誌、系統托盤與自動啟動。
核心思路:你需要盯住的 6 個欄位
Proxy Monitor 的一條記錄(後端結構體 ProxyRequestLog)裡,最實用的是下面這些欄位:
| 欄位 | 你用它回答什麼問題 |
|---|---|
status | 這次請求是成功還是失敗(200-399 vs 其他) |
url / method | 你到底打到了哪個端點(例如 /v1/messages、/v1/chat/completions) |
protocol | 這是 OpenAI / Claude(Anthropic) / Gemini 哪套協定 |
account_email | 這次請求最終用了哪個帳號(來自 X-Account-Email 回應標頭) |
model / mapped_model | 客戶端請求的模型名有沒有被「路由/映射」成另一個模型 |
input_tokens / output_tokens | 這次請求的 Token 用量(能提取到才有) |
先用「摘要」,需要時再點「詳情」
列表頁只展示摘要(不帶 request/response body),點開一條記錄才會去後端載入完整詳情,避免列表一次性拉太多大欄位。
跟我做:用一次呼叫跑通「監控閉環」
第 1 步:先製造一條「你確定會出現」的請求
為什麼 Proxy Monitor 只記錄反代服務收到的請求。先用一個最簡單的請求把「有沒有記錄」這件事驗證掉,後面才談篩選和詳情。
## 1) 探活(一定存在的端點)
curl "http://127.0.0.1:PORT/healthz"
## 2) 再請求一次 models(如果你開啟了鑑權,記得加 header)
curl "http://127.0.0.1:PORT/v1/models"## 1) 探活(一定存在的端點)
curl "http://127.0.0.1:PORT/healthz"
## 2) 再請求一次 models(如果你開啟了鑑權,記得加 header)
curl "http://127.0.0.1:PORT/v1/models"你應該看到:終端返回 {"status":"ok"}(或類似 JSON),以及 /v1/models 的回應(成功或 401 都行)。
第 2 步:開啟 Monitor 頁面並確認「錄製狀態」
為什麼 Proxy Monitor 有「錄製/暫停」開關。你需要先確認目前狀態,否則你可能一直在請求,但列表永遠是空的。
在 Antigravity Tools 裡開啟側邊欄的 API 監控看板(路由為 /monitor)。
頁面頂部會有一個帶圓點的按鈕:
┌───────────────────────────────────────────┐
│ ● 正在錄製 [搜尋框] [重新整理] [清除] │
└───────────────────────────────────────────┘如果你看到的是「已暫停」,點一下切換到「正在錄製」。
你應該看到:按鈕狀態變成「正在錄製」;列表裡開始出現剛才的請求記錄。
第 3 步:用「搜尋 + 快速篩選」定位一條記錄
為什麼 真實排障時,你通常只記得一個片段:路徑裡有 messages,或者狀態碼是 401,或者模型名裡有 gemini。搜尋框就是為這種記憶設計的。
Proxy Monitor 的搜尋,會把你的輸入當成一個「模糊關鍵字」,在後端用 SQL LIKE 匹配這些欄位:
urlmethodmodelstatusaccount_email
你可以試幾個典型關鍵字:
healthzmodels401(如果你剛好製造了一個 401)
也可以點「快速過濾」按鈕:僅錯誤 / Chat / Gemini / Claude / 繪圖。
你應該看到:列表只剩下你期望的那一類請求。
第 4 步:點開詳情,還原「請求報文 + 回應報文」
為什麼 列表只夠回答「發生了什麼」。要回答「為什麼」,你通常需要看完整請求/回應 payload。
點開任意一條記錄,會彈出詳情視窗。你可以重點檢查:
協定(OpenAI/Claude/Gemini)使用模型與映射模型使用帳號Token 消耗 (輸入/輸出)
然後用按鈕複製:
請求報文 (Request)回應報文 (Response)
你應該看到:詳情裡有兩塊 JSON(或文字)預覽;複製後你可以貼到工單/筆記裡做覆盤。
第 5 步:需要「從零開始重現」時,清空日誌
為什麼 排障時最怕「舊資料干擾判斷」。清空後重現一次,成功/失敗會非常清楚。
點擊頂部的「清除」按鈕,會彈出確認框。
這是不可逆操作
清除會刪除 proxy_logs.db 裡的所有記錄。
你應該看到:確認後列表清空,統計數字回到 0。
檢查點 ✅
- [ ] 你能在
/monitor看到自己剛發出的/healthz或/v1/models記錄 - [ ] 你能用搜尋框把某條記錄篩出來(例如輸入
healthz) - [ ] 你能點開一條記錄看到請求/回應報文,並複製出來
- [ ] 你知道清空日誌會直接刪除所有歷史記錄
踩坑提醒
| 場景 | 你可能會怎麼理解(❌) | 實際表現(✓) |
|---|---|---|
| "已暫停"= 完全沒有監控開銷 | 以為暫停後請求就不會被解析 | 暫停只影響「是否寫入 Proxy Monitor 日誌」。但請求/回應解析(包括流式資料的 SSE 解析)仍會發生,只是解析後的資料不被儲存。Token Stats 也仍會記錄(無論監控是否啟用)。 |
| 二進位/大封包日誌為空 | 以為是監控壞了 | 二進位請求/回應會顯示為 [Binary Request Data] / [Binary Response Data]。回應體超過 100MB 會標記為 [Response too large (>100MB)];請求體超過限制則不記錄。 |
| 想用 Monitor 找到「誰發起了請求」 | 以為能追溯到客戶端程序 | Monitor 記錄的是 HTTP 層資訊(方法/路徑/模型/帳號),不包含「呼叫方程序名」。你需要結合客戶端自身日誌或系統網路抓包定位來源。 |
| 啟用監控時 Token Stats 資料異常 | 以為是統計錯誤 | 監控啟用時,Token Stats 可能被記錄兩次(一次在監控函式開頭,一次在非同步寫入資料庫時)。這是目前版本的原始碼行為。 |
匯出與長期留存:先說清楚能力邊界
1) GUI 裡目前能做什麼
在目前版本的 Monitor UI(ProxyMonitor.tsx)裡,你可以:
- 搜尋/篩選/分頁瀏覽
- 點開詳情查看與複製 payload
- 清空全部日誌
但 沒有發現「匯出按鈕」(原始碼裡未看到相關 UI)。
2) 後端已經有什麼匯出能力(適合二次開發)
後端 Tauri 指令提供了兩種匯出方式:
export_proxy_logs(file_path):把資料庫裡「全部日誌」匯出為 JSON 檔案export_proxy_logs_json(file_path, json_data):把你傳入的 JSON 陣列美化後寫入檔案(要求輸入必須是陣列格式)
如果你要二次開發 GUI,或者寫一個自己的呼叫腳本,可以直接複用這些指令。
3) 最樸素的「匯出」:直接備份 proxy_logs.db
因為 Proxy Monitor 本質是 SQLite,你也可以把 proxy_logs.db 當成「排障證據包」一起備份(例如連同 token_stats.db)。資料目錄位置見 首次啟動必懂。
推薦閱讀
- 想弄清模型映射:模型路由:自定義映射、萬用字元優先順序與預設策略
- 想排查鑑權問題:401/鑑權失敗:auth_mode、Header 相容與客戶端設定清單
- 想把 Monitor 與成本統計結合:下一節會講 Token Stats(
token_stats.db)
本課小結
- Proxy Monitor 的價值是「可覆盤」:記錄狀態碼/路徑/協定/帳號/模型映射/Token 用量,並提供請求詳情
- 搜尋與快速篩選是排障入口:先縮小範圍,再點詳情看 payload
- 清空日誌是不可逆操作;匯出目前更偏向「二次開發能力」和「備份資料庫檔案」
下一課預告
下一課我們學習 Token Stats:成本視角的統計口徑與圖表解讀,把「感覺貴」變成可量化的最佳化。
附錄:原始碼參考
點擊展開查看原始碼位置
更新時間:2026-01-23
| 功能 | 檔案路徑 | 行號 |
|---|---|---|
| Monitor 頁面入口(掛載 ProxyMonitor) | src/pages/Monitor.tsx | 1-12 |
| Monitor UI:表格/篩選/詳情彈窗 | src/components/proxy/ProxyMonitor.tsx | 13-713 |
| UI:讀取設定並同步 enable_logging | src/components/proxy/ProxyMonitor.tsx | 174-243 |
| UI:切換錄製(寫入 config + set_proxy_monitor_enabled) | src/components/proxy/ProxyMonitor.tsx | 254-267 |
| UI:即時事件監聽(proxy://request)與去重 | src/components/proxy/ProxyMonitor.tsx | 273-355 |
| UI:清空日誌(clear_proxy_logs) | src/components/proxy/ProxyMonitor.tsx | 389-403 |
| UI:載入單條詳情(get_proxy_log_detail) | src/components/proxy/ProxyMonitor.tsx | 505-519 |
| 監控中介軟體:抓取請求/回應、解析 token、寫入 monitor | src-tauri/src/proxy/middleware/monitor.rs | 13-337 |
| ProxyMonitor:enabled 開關、寫 DB、發事件 | src-tauri/src/proxy/monitor.rs | 33-194 |
| 伺服器端掛載監控中介軟體(layer) | src-tauri/src/proxy/server.rs | 183-194 |
| Tauri 指令:get/count/filter/detail/clear/export | src-tauri/src/commands/proxy.rs | 180-314 |
| SQLite:proxy_logs.db 路徑、表結構與篩選 SQL | src-tauri/src/modules/proxy_db.rs | 1-416 |
| 監控設計說明(可能與實作有差異,以原始碼為準) | docs/proxy-monitor-technical.md | 1-53 |
關鍵常量:
MAX_REQUEST_LOG_SIZE = 100 * 1024 * 1024:監控中介軟體最大可讀取的請求體(超過會讀取失敗)MAX_RESPONSE_LOG_SIZE = 100 * 1024 * 1024:監控中介軟體最大可讀取的回應體(用於圖片等大回應)
關鍵函式/指令:
monitor_middleware(...):在 HTTP 層採集請求與回應,並呼叫monitor.log_request(...)ProxyMonitor::log_request(...):寫入記憶體 + SQLite,並透過proxy://request事件推送摘要get_proxy_logs_count_filtered(filter, errors_only)/get_proxy_logs_filtered(...):列表頁篩選與分頁get_proxy_log_detail(log_id):載入單條日誌的完整 request/response bodyexport_proxy_logs(file_path):匯出全量日誌到 JSON 檔案(目前 UI 未暴露按鈕)