錯誤追蹤站(Error Tracking Site)¶
功能群:系統管理|本檔為非頁面 spec(產品內無 route):錯誤追蹤站是隨落地版安裝的第三方站台(Bugsink 2.6.1),掛在前門的
/bugsink/子路徑下,不在產品選單裡。本頁記「哪些錯誤會進站、怎麼進、誰看得到、怎麼設定」,13 節中不適用的標 N/A。事實基準:2026-10-10 從
core/plugins/api_log.py、api/error_tracking/、app/error_tracking/、common/authz/caller.py、core/plugins/_agent_error_tracking.py、infra/support/diag_event_fetcher.py、scripts/installer/bugsink.sh、scripts/installer/guidantai_bugsink.sh、docker/production/docker-compose.yml、FEsrc/config/errorTracking.js、jedi-logjedi_api_log/error_tracking/掃出。
變更紀錄¶
| 日期 | FR | 變更 |
|---|---|---|
| 2026-10-10 | FR-133(1.26.0 之後) | 首版:落地版內建錯誤追蹤站(三個專案、裝機自動建站)、前端與 Agent 經登入後轉送端點回報、預設全收、遮罩補位置參數、診斷包改撈 Bugsink |
1. 功能描述¶
每套 Guidant AI 落地版自帶一個錯誤追蹤站:系統各元件發生錯誤時,把當下的堆疊、變數、踩到的使用者、請求編號送進這個站台,原廠人員到客戶現場打開站台就能看出錯在哪一行、當時的值是什麼。站台只在客戶自己的機器上,事件不會送到原廠。
站台由安裝程式在首次安裝與每次升級時自動建好(管理員帳號、三個專案、連線字串),裝機人員不必輸入任何東西。
主要使用者:原廠維運與開發人員(到現場或經客戶授權遠端連入)。客戶的一般使用者看不到這個站台,產品選單也沒有入口。
角色速覽
| 角色 | 一句話 |
|---|---|
| 原廠人員 | 用站台管理員帳號登入 https://<主機>/bugsink/ 看錯誤 |
| 客戶維運人員 | 用 guidantai 子命令清資料、改管理員密碼、調收件開關 |
| 產品使用者 | 不接觸站台;登入後在瀏覽器踩到的前端錯誤會自動回報 |
1.1 功能總覽¶
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 站台入口 | https://<主機>/bugsink/,經前門 443 轉進,不另開埠 |
前門 nginx location /bugsink/ |
§5、§11 |
| 2 | 管理員登入 | 帳號固定 admin@guidantai.com;密碼存在資料目錄 bugsink-admin.secret(root、600) |
Bugsink 登入頁 | §3 |
| 3 | 三個專案 | guidant-ai/guidant-ai-fe/guidant-agent 分開收 |
Bugsink 專案列表 | §4 |
| 4 | 後端錯誤自動回報 | api/socketio/worker 與證據分類沙箱走內網直送 | 各容器啟動時初始化 | §10 |
| 5 | 前端錯誤回報 | 登入後才啟用,經後端轉送端點 | FE src/config/errorTracking.js |
UC-ETS-02、§6 |
| 6 | Agent 錯誤回報 | Agent 從註冊/心跳回應拿設定,經同一支轉送端點 | evidence-agent 1.4.2 起 | UC-ETS-03 |
| 7 | 收件開關 | 4xx 使用者失誤、ERROR log 行是否另成事件,預設全收 | guidant.env 兩個變數 |
§4 |
| 8 | 事件遮罩 | 憑證欄位、Bearer 值、無名位置參數送出前遮掉 | jedi-log、Agent、分類器各一份 | §12 |
| 9 | 請求編號對回 log | 事件 tag request_id 與 api log 每行的編號相同 |
事件 tags | UC-ETS-04 |
| 10 | 診斷包撈事件 | guidantai diag 經讀取 API 撈三個專案的事件 |
infra/support/diag_event_fetcher.py |
§11 |
| 11 | 維運子命令 | bugsink-vacuum、bugsink-set-password |
scripts/installer/guidantai_bugsink.sh |
§13 |
UC 展開 1、5、6、9 四條核心路徑。
2. Use Case¶
角色速覽:同 §1。
視覺慣例:本頁 UC 流程以表格逐步列出(無獨立流程圖),例外分支寫在「例外」列。
UC-ETS-01 後端程式錯誤進站¶
| 角色 | 前置條件 | 產出後置 |
|---|---|---|
| 系統(api/socketio/worker/沙箱) | 安裝程式已寫好 SENTRY_DSN |
guidant-ai 專案出現一筆事件,含堆疊、release、request_id、使用者帳號 |
- 請求或背景工作拋出例外。
- 送出前規則(
core/plugins/api_log.py::_extra_before_send):收件開關判斷是否丟棄 4xx → 補使用者帳號 → 補request_id→ 每分鐘節流。 - 遮罩(jedi-log
scrub_event)後經內網http://guidant-bugsink:8000送出。
例外:站台拒收(Host 不在允許清單)時送件端不報錯,事件靜默消失;節流丟棄的筆數寫進 api log 一行警告。
UC-ETS-02 前端錯誤進站(登入後)¶
| 角色 | 前置條件 | 產出後置 |
|---|---|---|
| 已登入使用者的瀏覽器 | SENTRY_DSN 與 SENTRY_PUBLIC_DSN 都有值 |
guidant-ai-fe 專案出現事件,release guidant-ai-fe@<版號>+<commit>、tag request_id 為最近一次 API 回應的編號 |
- 登入後載入能力點成功 → 前端打
GET /api/1.0/error-tracking/config;enabled=false就不載入 SDK。 - 前端錯誤發生 → SDK 把事件 POST 到
/api/1.0/error-tracking/tunnel,帶Authorization。 - 後端檢查身分、大小、目的地專案、次數 → 轉進站台內網位址,回 204。
例外:未登入 401;目的地專案不符 400(ERT_400002);超過 1 MiB 413;超頻 429。前端遇 401 視同 429,停送 60 秒。登出時關閉 SDK。
UC-ETS-03 Agent 錯誤進站¶
| 角色 | 前置條件 | 產出後置 |
|---|---|---|
| 受檢主機上的檢測 Agent(1.4.1 起) | 雲端已升到含轉送端點的版本;SENTRY_AGENT_DSN 有值 |
guidant-agent 專案出現事件,tag agent_id 分台 |
- Agent 註冊或心跳 → 回應的
data.error_tracking帶{enabled, environment, dsn}。 enabled為真才初始化 SDK;事件經 Agent 自己的轉送 transport,以 Agent 通行證 POST 到雲端轉送端點。- 後端依通行證判定是 Agent,只准送
guidant-agent專案。
例外:通行證被拒(401/403)時 Agent 記住那顆通行證,換新前不再送;註冊完成前的錯誤收不到。
UC-ETS-04 從事件對回 api log¶
| 角色 | 前置條件 | 產出後置 |
|---|---|---|
| 原廠人員 | 事件 tags 有 request_id(形如 req-xxxxxxxx) |
找到同一次請求前後的完整 log |
- 站台事件頁看 tags 的
request_id。 - 在主機上
sudo guidantai logs guidant-api或診斷包內的 log,以該編號搜尋。 - 也可
sudo guidantai diag --request-id <編號>只打包該次請求相關的紀錄與事件。
背景排程沒有請求脈絡,request_id 為 - 或 job-<名稱>-<8 碼>。
3. 權限矩陣¶
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 開站台、看事件 | N/A(不在產品內) | Bugsink 自己的帳號登入;SINGLE_USER=True、不開放註冊 |
compose guidant-bugsink 環境變數 |
GET /error-tracking/config |
登入後才呼叫 | jwt_required;Agent 通行證打它回 401;不掛能力點 |
api/error_tracking/routes/error_tracking_route.py |
POST /error-tracking/tunnel |
SDK 自帶 token | jwt_or_agent_pass_required:使用者 JWT 或 Agent 通行證擇一,兩條路各自驗簽;不掛能力點 |
common/authz/caller.py |
| 只能送自己那個專案 | — | 使用者只准 SENTRY_PUBLIC_DSN 的專案、Agent 只准 SENTRY_AGENT_DSN 的專案 |
app/error_tracking/service/error_tracking_app_service.py |
| 清資料、改管理員密碼 | — | 主機 root(sudo guidantai ...) |
scripts/installer/guidantai_bugsink.sh |
管理員密碼全客戶同一組,出包時烙進安裝包;升級不改既有密碼,單台可用 bugsink-set-password 改掉。站台只在客戶內網可達是這組共用密碼的前提。
4. 狀態機與前置條件¶
無狀態機。站台與各專案的存在與收件行為由下列條件決定:
三個專案各收誰
| 專案 | 收誰的錯誤 | 識別 |
|---|---|---|
guidant-ai |
api、socketio、worker、證據分類沙箱 | 沙箱事件帶 tag app=guidant-classifier |
guidant-ai-fe |
已登入使用者的瀏覽器 | release guidant-ai-fe@... |
guidant-agent |
所有受檢主機的檢測 Agent | tag agent_id |
哪些元件的錯誤會進、哪些不會
| 元件 | 進站? | 路徑 |
|---|---|---|
| api/socketio/worker | 會 | 內網直送 |
| 證據分類沙箱 | 會 | 只通站台的專屬內網網段 guidant-sandbox-errors |
| 前端(登入後) | 會 | 經後端轉送端點 |
| 檢測 Agent(1.4.1 起) | 會 | 經後端轉送端點 |
| 前端(登入前,如登入頁本身的錯誤) | 不會 | 轉送端點要登入 |
| Agent 註冊完成前 | 不會 | 設定從註冊/心跳回應拿 |
guidant-db-init(資料庫初始化與升級遷移) |
不會 | 未接錯誤追蹤,看安裝畫面與 docker logs |
收件開關(guidant.env)
| 變數 | 預設 | 意思 |
|---|---|---|
ERROR_TRACKING_CAPTURE_CLIENT_ERRORS |
true |
401/403/404 這類使用者失誤也送站台。站台被灌滿時設 false,只收程式錯誤 |
ERROR_TRACKING_LOG_EVENTS |
true |
每一行 ERROR 等級的 log 各自成事件。設 false 時 log 只當事件的前情紀錄 |
ERROR_TRACKING_RATE_LIMIT |
60 |
每個後端行程每分鐘送出上限;轉送端點也用它當每人、每台 Agent 每分鐘上限 |
兩個開關只認 1/true/yes 與 0/false/no,打錯字回預設值。改完要 sudo guidantai start 才生效(restart 不重讀設定檔)。
站台建不起來的三種情形(安裝只警告、不擋):安裝包沒有管理員密碼檔、讀不到站台網址、stack 內沒有 guidant-db(客戶自備資料庫)。此時站台不建、SENTRY_DSN 留空,產品照常運作,只是錯誤不收。
5. UI 設計¶
N/A(站台介面是 Bugsink 本身,不由本產品設計)。產品前端沒有任何對應畫面;錯誤回報對使用者完全無感。
站台常用的三個畫面:專案列表(三個專案)、某專案的問題列表(同一個錯誤歸成一個 issue,顯示次數與最後發生時間)、單筆事件(堆疊含每一層的變數、tags、使用者、前情紀錄)。
6. API 規格¶
6.1 總清單¶
| 分類 | Method + Path | 說明 |
|---|---|---|
| 前端設定 | GET /api/1.0/error-tracking/config |
登入後取前端的連線設定 |
| 轉送 | POST /api/1.0/error-tracking/tunnel |
收前端或 Agent 的 envelope,檢查後轉進站台 |
| Agent 設定(附帶) | 既有註冊、心跳回應的 data.error_tracking |
主專案在 Agent blueprint 疊 after_request 補欄位 |
6.2 GET /error-tracking/config 回應 data¶
| 欄位 | 型別 | 說明 |
|---|---|---|
enabled |
Bool | SENTRY_DSN 與 SENTRY_PUBLIC_DSN 都有值才為 true |
dsn |
Str | SENTRY_PUBLIC_DSN;enabled=false 時不帶 |
environment |
Str | ENV |
出處:api/error_tracking/routes/error_tracking_route.py、app/error_tracking/service/error_tracking_app_service.py。
6.3 POST /error-tracking/tunnel¶
請求本體是 Sentry envelope(application/x-sentry-envelope),第一行 JSON 必須含 dsn。
| 檢查 | 結果 |
|---|---|
呼叫者那個專案的對外 DSN 或 SENTRY_DSN 未設 |
204,不轉 |
| 長度大於 1 MiB | 413 ERT_413001 |
第一行解析失敗或沒有 dsn |
400 ERT_400001 |
dsn 的 project id 或 public key 與呼叫者那個專案不同(不比主機) |
400 ERT_400002 |
| 同一使用者/同一台 Agent 每 60 秒超過上限 | 429 ERT_429001 |
| 通過 | 轉到「SENTRY_DSN 的主機+呼叫者專案編號」的內網收件位址,回 204;上游失敗只記 log,仍回 204 |
送往主機永遠取設定值,不採 envelope 裡的位址——否則這支端點就是開放轉發器。節流以 Redis 計數,Redis 掛掉時放行並記警告。錯誤碼定義在 common/code/error_tracking_error_code.py,FE error-code.json 同步。
6.4 Agent 註冊/心跳回應附帶欄位¶
data.error_tracking = {enabled, environment, dsn},dsn 取 SENTRY_AGENT_DSN,它或 SENTRY_DSN 缺一則 enabled=false。補欄位失敗只記 log,不影響註冊與心跳。出處:core/plugins/_agent_error_tracking.py。
7. 前端檔案地圖¶
| 檔案 | 角色 |
|---|---|
compliance-manager-fe/src/config/errorTracking.js |
登入後初始化、自帶 token 的 fetch、附 request_id、登出關閉 |
compliance-manager-fe/src/config/api/api.js |
ERROR_TRACKING_CONFIG、ERROR_TRACKING_TUNNEL 兩個端點常數 |
compliance-manager-fe/nginx.onprem.conf |
前門 location /bugsink/:先 rewrite 剝前綴再轉內網 8000 |
8. 後端檔案地圖¶
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 前端設定、轉送 | api/error_tracking/routes/error_tracking_route.py |
app/error_tracking/service/error_tracking_app_service.py |
infra/error_tracking/envelope_forwarder_client.py |
| 雙身分守門 | — | — | common/authz/caller.py(jwt_or_agent_pass_required、current_agent_uid) |
| 後端初始化與送出前規則 | — | — | core/plugins/api_log.py(init_error_tracking、_extra_before_send、capture_handled_exception) |
| Agent 回應補欄位 | — | — | core/plugins/_agent_error_tracking.py |
| 診斷包撈事件 | — | — | infra/support/diag_event_fetcher.py |
| 安裝建站 | — | — | scripts/installer/bugsink.sh |
| 維運子命令 | — | — | scripts/installer/guidantai_bugsink.sh |
9. DB¶
本產品資料庫 guidant_ai 不新增任何表。站台資料放在同一台 guidant-db 的獨立資料庫 bugsink,帳號 bugsink 只擁有這個庫,對 guidant_ai 無任何權限;表結構由 Bugsink 自己的遷移管理,產品程式碼不讀寫。
10. 頁面邏輯與資料對應¶
連線字串由安裝程式寫入
| 變數 | 檔案 | 值 | 誰讀 |
|---|---|---|---|
SENTRY_DSN |
guidant.env |
guidant-ai 專案、內網形狀 http://<key>@guidant-bugsink:8000/<id> |
後端三容器;轉送端點取它的主機 |
SENTRY_PUBLIC_DSN |
guidant.env |
guidant-ai-fe 專案、對外形狀 https://<key>@<主機>/bugsink/<id> |
前端設定端點、轉送端點比對 |
SENTRY_AGENT_DSN |
guidant.env |
guidant-agent 專案、對外形狀 |
Agent 回應、轉送端點比對 |
BUGSINK_API_TOKEN |
guidant.env |
跨三專案唯讀權杖 | 診斷包 |
GUIDANT_SENTRY_DSN |
.env |
同 SENTRY_DSN |
compose 帶給證據分類沙箱(沙箱不讀 guidant.env,那份檔含資料庫與登入金鑰) |
GUIDANT_BUGSINK_SECRET_KEY、GUIDANT_BUGSINK_DB_PASSWORD |
.env |
隨機產生,缺了才補 | compose |
GUIDANT_BUGSINK_BASE_URL、GUIDANT_BUGSINK_ALLOWED_HOSTS |
.env |
每次安裝與升級依 SYSTEM_URL 重算;允許主機名會保留既有清單多出的主機名 |
compose |
SENTRY_DSN 預先填了外部站台時,安裝程式保留不動並警告,GUIDANT_SENTRY_DSN 留空。
事件內容:堆疊(每一層的區域變數)、例外訊息、release(guidant-ai@<版號>+<commit>)、environment、tag request_id、tag app、使用者登入帳號(不含 user_id)、前情紀錄(同一請求先前的 log)。不含:HTTP 回應內容、cookie、Authorization 等四個憑證標頭(整支拿掉)、SDK 自動附的個資(send_default_pii=False)。
11. 背景行為與外部依賴¶
| 類型 | 內容 |
|---|---|
| 第三方元件 | bugsink/bugsink:2.6.1(PolyForm Shield 授權),出貨包內含 image tar,digest 釘死 |
| 容器 | guidant-bugsink:不對外開埠,內網 8000;mem_limit 1g、cap_drop ALL |
| 網路 | default 網段(DB、前門)+ guidant-sandbox-errors(internal: true,成員只有沙箱與站台) |
| 保留 | 每個專案最多 1 萬筆事件,超過由 Bugsink 收件時依重要性淘汰;不設天數上限 |
| 不送原廠 | 站台只在客戶主機;沒有任何對外回傳(PHONEHOME=false) |
| 告警 | 無(不寄信、不打 webhook) |
| 診斷包 | guidantai diag 用 BUGSINK_API_TOKEN 打 /api/canonical/0/issues/,三個專案編號取三支 DSN 的聯集;寫成 events/bugsink-events.json。讀取 API 不能依 tag 篩,帶 --request-id 時逐筆比對,請求總數上限 80。取不到時 manifest 寫明原因,不讓整包失敗 |
| jedi-* | jedi-log 1.3.6(init_error_tracking、capture_exception、scrub_event);jedi-evidence-classification 1.6.5(沙箱自帶的初始化與遮罩) |
| socket | 無 |
12. 邊界情況與已知坑¶
- 站台拒收時送件端不報錯:Host 不在
ALLOWED_HOSTS、或站台網址換了沒重跑升級,事件靜默消失。驗收一律以「站台上看到事件」為準,不看後端 log 的「已啟用」。 - 改了站台網址要重跑升級:改
guidant.env的SYSTEM_URL後跑sudo ./install.sh --upgrade,站台網址、允許主機名、兩支對外 DSN 會一起重寫。只改不跑,前端與 Agent 的事件送不進去。 - 用 IP 裝、外面再掛網域:網域不在允許清單時,用網域開站台回 400(錯誤收集本身不受影響,因為後端、前端、Agent 都走內網)。在
.env的GUIDANT_BUGSINK_ALLOWED_HOSTS補上網域後sudo guidantai start;之後升級會保留這個網域。站台內的連結仍指向SYSTEM_URL的主機。 - 改設定要用
start:restart只重啟現有容器、不重讀設定檔。 - 遮罩規則有三份複製品:jedi-log(後端)、evidence-agent
core/error_tracking/masking.py、jedi-evidence-classificationdocker/error_tracking.py。三份都遮憑證欄位名、Bearer值,以及堆疊裡的args/kwargs與含字串的無名清單(後者是為了包裝層以位置參數轉手的密碼)。代價是除錯時看不到args內容。改一份要同步另外兩份。 - 全收模式下登入失敗會進站:預設全收,打錯密碼也會產生 401 事件;密碼由位置參數遮罩擋下,前提是 jedi-log 1.3.6 以上。
- 容器重啟會丟掉還沒處理的事件:Bugsink 的收件佇列在容器內暫存,升級或重啟那幾秒進來的事件可能遺失。
bugsink-vacuum不刪舊事件:站台沒設事件天數上限,vacuum 只清孤兒檔案與已刪問題的殘留;事件量由每專案 1 萬筆上限封頂。- 管理員密碼共用一組:任何一台安裝目錄外洩,所有客戶站台都能用這組登入;緩解是站台只在內網可達、單台可用
bugsink-set-password改掉。 - 升級順序先雲端、後 Agent:Agent 從雲端回應拿設定,雲端沒升時 Agent 只是不啟用,不會壞。
- 弱點白名單到期日:bugsink 2.6.1 image 有三條 Critical 上游未修,登記在
scripts/build/vuln-allowlist.txt,到期 2026-12-31;到期前沒換版,出包會被弱點關卡擋下。
13. 開發與驗證¶
- DEV:本機
.env沒設SENTRY_DSN時錯誤追蹤整支不啟用,api log 留一行「錯誤追蹤未啟用」。要測就在本機另起 Bugsink 容器、把 DSN 指過去(步驟見 FR-133b0-findings.md)。 - 落地版站台:
https://<主機>/bugsink/,帳號admin@guidantai.com,密碼見主機<資料目錄>/bugsink-admin.secret(需 root)。 - 維運子命令:
sudo guidantai bugsink-vacuum(印清理前後事件筆數)、sudo guidantai bugsink-set-password(互動輸入兩次、密碼不上命令列,成功後同步覆寫密碼檔)。 - 快速驗收:登入後在瀏覽器 console 故意 throw →
guidant-ai-fe專案收到;打一支故意 500 的端點 →guidant-ai專案一分鐘內收到;未登入curl -X POST https://<主機>/api/1.0/error-tracking/tunnel→ 401。 - 守衛測試:
test/security/code/test_route_guard_inventory.py(兩支端點登記在豁免清單)、test/security/code/test_security_shape_external_calls.py(轉送的對外呼叫登記在白名單)、test/test_api_log_client_error_filter.py(收件開關與預設值)。 - 相關文件:錯誤追蹤接入規範(開發者,BE repo
docs/claude/error-tracking-integration.md)、落地版手冊 四、日常維運、FR-133 設計文件(BE repodocs/features/FR-133-2610-bugsink-bundled-error-tracking/design.md)、前作 FR-071(錯誤追蹤接線與診斷包)。