FR-133 · 需求討論稿 · 2026-10-09(v2 · D1~D8 全部定案)

客戶機器上就看得到程式錯誤

Bugsink 打進安裝包,裝機全自動設好,後端與前端錯誤都收得到

產品要進客戶 UAT(使用者驗收測試),客戶環境不受我們控制。出錯時要在客戶自己的機器上就看得到:錯在程式哪一行(堆疊)、當下變數值、誰踩的、哪一筆請求。決策者選定 Bugsink(單一容器、與 Sentry SDK 相容的錯誤追蹤站)隨產品出貨,裝機時自動建好帳號、專案與連線字串,裝機人員不必開任何畫面、不必輸入任何東西。本稿盤點現況四個缺口、記錄已定案的八項決策與連線面,並拆成六棒。

D1~D8 與過濾開關全部定案 只用 443:子路徑看站、登入後轉送前端錯誤 B0 必測:子路徑、轉送、主機名 六棒:A/B0/B/C/D/E 跨 3 repo:BE/FE/jedi-log 陷阱:一次登入失敗在站台變三條
§1

分工概述(30 秒版)

整案一句話:每套 Guidant AI 都自帶一個錯誤追蹤站,裝好就能用;後端與前端的程式錯誤自動送進去,業務性的「使用者打錯」不送。

給非工程讀者的背景:程式出錯時,工程師要知道錯在哪一行、當時變數是什麼值、是誰、在做什麼。錯誤追蹤站(Error tracking,像一個專收錯誤報告的信箱網站)會把同一種錯誤歸成一條、附上這些資訊。後端已經會「寄信」,缺的是每個客戶機器上要有一個收信的站,以及「哪些該寄、哪些不該寄」的篩選。

六棒,A 與 B0 先跑、可平行

A(過濾開關)與 B0(Bugsink 實測)互不相干,可同時開工。B 等 B0 的實測結果;C、D 等 B;E 收尾文件等全部。

棒 做什麼 產出 完成怎麼判定(決策者檢查法)
A 過濾開關 業務錯誤(打錯密碼、查無資料這類 4xx)預設不送站台;logger.error 那一行 log 預設不另開一條,只當附帶紀錄 兩個環境變數、jedi-log 1.3.4、後端釘新版 DEV 開著站台,故意打錯密碼登入三次 → 站台一條都不出現;再故意打一支會 500 的 API → 站台只出現一條,不是三條
B0 可行性實跑 在 DEV 起 Bugsink 2.6.1,實測「用指令建專案 → 讀出連線字串 → 後端送得進去 → 用 API 讀得回來」整條,另外必測三件:站台能不能掛在 443 的子路徑、前端錯誤經後端轉送進不進得去、主機名從哪讀與改了會怎樣 一段可直接搬進 install.sh 的 shell 片段+實測紀錄(含子路徑通或不通的結論) 打開實測紀錄,清單九項每項都有實際輸出貼在上面;照片段在空白機器重跑一次,站台上看得到後端送進來的錯誤
B 進出貨包 安裝包多帶一顆 Bugsink;首次安裝自動建管理員、專案、連線字串;升級時缺了才補;維運指令加清理與改密碼 compose、bundle、install.sh、guidantai 子命令 拿新包在乾淨機器裝好 → 瀏覽器開 https://<主機>/bugsink/(B0 子路徑測不通時改 https://<主機>:8443)用管理員登入,看得到 guidant-ai 專案 → 在產品故意打一支會 500 的 API → 一分鐘內站台出現該筆,點進去有堆疊
C 前端錯誤接回 前端的程式錯誤經後端一支「登入後才能打」的轉送端點送進同一個站,帶版號與 commit,能跟後端那一筆串起來 FE 接線、後端轉送端點、登入後端點多帶 DSN 一欄 登入產品後在瀏覽器故意觸發一個前端錯誤 → 站台出現該筆,版本欄有版號+commit、有 request_id;登出狀態下打轉送端點回 401
D 診斷包改用 Bugsink API 診斷包撈錯誤事件改打 Bugsink 自己的 API diag_event_fetcher.py 改寫 在客戶機器跑一次診斷包匯出 → 解開包,錯誤事件那一份有內容,與站台上看到的一致
E 文件 設定範本、部署文件、客戶手冊、release note 四份文件 客戶手冊裡找得到「站台怎麼進、密碼在哪、事件保留多久、改設定後要用 start 不是 restart」四件事

依賴:A、B0 平行 → B → C、D 平行 → E。


§2

需求背景

後端已經會送,缺的是收的站

後端錯誤追蹤的接線已存在,v1.26.0 起 sentry-sdk(送錯誤報告的程式庫)隨包內建:

落點 現況
主專案 core/plugins/api_log.py:234 init_error_tracking() 讀環境變數 SENTRY_DSN(DSN=連線字串,告訴程式錯誤要送去哪個站、哪個專案),交給 jedi-log 套件
jedi-log 1.3.3 jedi_api_log/error_tracking/__init__.py 真正初始化 SDK 的地方
三個後端容器 api/socketio/worker 都經 create_app 走同一條,一次接好三個都生效
遮罩 masking.py 送出前先過濾:憑證欄位遮掉、四支敏感 headers 整支拿掉、堆疊裡的區域變數也掃
節流 每分鐘 60 筆(ERROR_TRACKING_RATE_LIMIT)

遮罩與節流都在「送出前」(before_send)做,跟收的站是哪一套無關——換成 Bugsink 不必重做。

現況四個缺口(皆經實檔查證)

🔴 缺口一:一次登入失敗,站台上變三條

打錯一次密碼,站台會出現三條不同的 issue(issue=站台上歸成一類的錯誤):

  1. jedi-iam login_domain_service.py:143 的 logger.error——SDK 的 logging 整合會把 error 等級的 log 當成獨立事件
  2. UnauthorizedError 這個例外本身
  3. jedi-common handler.py:62 錯誤處理器再 logger.error 一次

不只登入:jedi_common/handler/exception.py 的 ClientError 家族八種(BadRequest/Unauthorized/Forbidden/NotFound/MethodNotAllowed/Conflict/PreconditionFailed/TooManyRequests)全部會進站台。UAT 時使用者的正常操作失誤會淹掉真正的程式錯誤,還會吃光每分鐘 60 筆的節流額度——真的 500 反而送不進去。

🔴 缺口二:前端錯誤回報已整個拆掉

2026-10-05 資安項 U3-⑤(CM-2495,FE 7e136266、BE 4b3a04334)拆掉了前端的 @sentry/vue。原因:連線字串由免登入端點 /client-config 供給,另有免登入的 /error-tunnel 轉送器,兩者被判為任何人不用登入就能打的攻擊面。守衛測試 test/security/code/test_version_module_no_error_tracking_endpoints.py 擋這兩支復活。

所以前端接回時不能走原路,見 D4。FR-071 設計稿(design.md 第 247、341、650 行附近)有「前端 release 帶版號+commit、用 trace 標頭串前後端」的做法可沿用。

缺口三:診斷包撈事件的 API 對不上

infra/support/diag_event_fetcher.py 是對著 GlitchTip 的 Sentry 式 API(/api/0/organizations/<org>/issues/)寫的。Bugsink 的讀取 API 不是這個形狀(見下節),要改寫。

缺口四:改了設定用 restart 不會生效

POC(189)實例:在 guidant.env 加了 SENTRY_DSN 後跑 guidantai restart,不生效。原因是 restart 底層是 docker compose restart,只重啟容器、不重讀設定檔;要用 guidantai start(up -d,會重建容器)。FR-132-STATE.md 第 19 行與 release note v1.26.0 §2b 的寫法要修正。


§3

Bugsink 是什麼、已知事實

面向 事實
版本與大小 image bugsink/bugsink:2.6.1(2026-09-25),amd64 壓縮約 78 MB,多架構
組成 單一容器,自帶背景處理程式(monofy 同時跑網站 gunicorn 與背景 snappea),不需要 Redis
執行身分 基底 python:3.12-slim,uid 14237,資料目錄 /data
資料庫 DATABASE_URL=postgresql://...,Postgres ≥ 14
佇列 snappea 的工作佇列是 SQLite,寫死在 /tmp——容器重啟會丟掉還沒處理完的事件
必填設定 SECRET_KEY(≥ 50 字)、BASE_URL(決定 DSN 長相,不可有結尾斜線)
反向代理 要送 Host/X-Forwarded-Proto/X-Real-IP 三個 header,設 BEHIND_HTTPS_PROXY=True;上傳上限 client_max_body_size ≥ 20M
管理員 CREATE_SUPERUSER=email:password,只在資料庫裡還沒有任何使用者時建
專案與 token 沒有建專案的指令或環境變數(官方 issue #240 開著無回應);API token 官方只能從畫面建
DSN 組成 build_dsn(BASE_URL, project.id, sentry_key.hex);sentry_key 是建專案時產的隨機 UUID,可從資料庫讀出
註冊 USER_REGISTRATION=CB_NOBODY 關閉自行註冊;SINGLE_USER=True 為單人模式
保留 每專案預設 1 萬筆(smart retention,依重要性挑留);MAX_EVENT_AGE_DAYS 需另排程跑 bugsink-manage vacuum
速率限制 預設 5 分鐘 1000 筆
讀取 API /api/canonical/0/:issues/?project=、events/?issue=、events/{id}/stacktrace/;Bearer token;2.6.0 起 token 帶權限(issues:read/events:read)。Sentry 相容的只有「收事件」那一面
收事件 /api/<project_id>/envelope/,相容 sentry-sdk 2.x;跨網域(CORS)只知 1.3.0 起有支援,細節查不到
告警 觸發:新 issue/舊錯誤復發/取消靜音;管道:Email(SMTP 變數)、Slack/Teams/Telegram/自訂 webhook;ALERTS_WEBHOOK_DENY_NON_GLOBAL 預設擋內網 webhook
升級 容器啟動時自動跑資料庫遷移,換 tag 即可;健康檢查 /health/ready,image 內建 HEALTHCHECK 寫死 8000 埠
授權 PolyForm Shield 1.0.0:可商用散布,不得拿去做與 Bugsink 競爭的產品

§4

已定案(決策者 2026-10-09)

以下全部已拍板,本段為記錄,本案沒有待決策項。

共同原則:客戶防火牆常只開 443,所以客戶需要連的東西一律走 443,不要求客戶多開 port。

# 決策 內容 理由
D1 自動建專案的做法 用 bugsink-manage shell 跑 Django ORM(Bugsink 內部的資料存取層)建 team/project/API token;DSN 用 SQL 從資料庫讀出,寫進 guidant.env 的 SENTRY_DSN 官方沒有建專案的指令。跟著 Bugsink 自己的資料模型走,比直接寫 SQL 插資料穩,版本升級時欄位異動由它自己吸收
D2 站台位址 優先掛在 443 的子路徑 https://<客戶主機>/bugsink/(nginx 轉給容器);備案才對外開 8443(BASE_URL=https://<客戶主機>:8443),兩者都沿用 stack 既有 HTTPS 憑證。Bugsink 的 BASE_URL 會推導 FORCE_SCRIPT_NAME(Django 用來支援子路徑的設定),代表有子路徑支援但未實證——B0 必測,測通就不開 8443,測不通才退 8443 並在手冊寫明要客戶開 port 客戶不必多開 port;8443 留作退路,不讓子路徑的不確定性卡住整案
D3 授權 PolyForm Shield 可接受,不過法務 本產品是合規管理系統,不是錯誤追蹤產品,不構成競爭
D4 前端錯誤怎麼送 一律走後端轉送,不做瀏覽器直送。FE @sentry/vue 設 tunnel(SDK 的轉送選項)指向後端一支登入後才能打的端點,後端原樣轉給 Bugsink 容器(docker 內網直連)。DSN 由登入後既有端點的回應多帶一欄取得。不復活免登入的 /error-tunnel//client-config,守衛測試照擋 轉送只依賴 443 與登入狀態,不必驗跨網域(CORS)。代價是登入前的前端錯誤收不到,決策者接受
D5 管理員帳密 帳號固定 admin@guidantai.com;密碼所有客戶同一組,build 時烙進安裝包(CI 的遮罩變數 → 包內一個權限 600 的檔),install.sh 讀它建管理員,並另存一份到 guidant.env 同目錄的檔案。裝機人員不輸入任何東西 裝機人員不具備操作能力;原廠維護時要能直接登入。決策者明確排除「每台隨機產生」與「install.conf 自帶」兩案
D6 資料庫落點 stack 內 guidant-db 另開 bugsink 庫與獨立帳號,不用 SQLite 不多起一顆 Postgres;獨立庫與帳號在權限上與產品資料互不相通;官方不建議 SQLite 放在 volume
D7 事件保留 沿用預設每專案 1 萬筆(挑重要的留),不設天數上限;guidantai bugsink-vacuum 子命令手動清 UAT 期間錯誤的價值在「是不是一直發生」,砍天數反而看不出趨勢;筆數上限已把磁碟用量封頂
D8 告警 第一版不做,靠人登入看站台;Email/webhook 列為下一版 第一版目標是「裝得起來、收得到」;告警牽涉客戶 SMTP 有沒有設、寄給誰,且客戶內網多半連不到 Slack/Teams
過濾 送不送的開關 做成環境變數,預設只收程式錯誤、可開回全收(細節見拆分表棒 A) 解掉缺口一;保留全收是為了某些客戶要查「為什麼一直 403」時能臨時打開

D5 的已知風險(如實記錄,決策已定)

同一組密碼烙在每一份安裝包裡。任何一台客戶機器的安裝目錄外洩,所有客戶的錯誤追蹤站都能用這組登入——站上有堆疊與變數值(已遮罩,但不是零業務資料)。緩解靠兩件事:站台入口只在客戶內網可達、維運指令提供改密碼子命令(棒 B),客戶或原廠可在單台上改掉。

明確不做:原廠集中收錯誤(本案是每台客戶機器自架,用不到)、讓客戶自訂 DSN 的畫面、站台設定畫面。


§5

連線面

誰連誰、走哪條路。客戶需要放行的只有 443。

誰 連到哪 走哪條路 經不經客戶防火牆
後端三容器(api/socketio/worker) Bugsink 收事件端點 docker 內網直連容器(http://guidant-bugsink:8000),DSN 指向內網位址 不經
客戶瀏覽器(已登入) 後端轉送端點 → Bugsink 443 → 後端(驗登入)→ docker 內網 → Bugsink 只經 443
客戶瀏覽器(未登入) — 轉送端點回 401,錯誤不收 —
原廠人員看站台 Bugsink 網頁介面 443 子路徑 https://<主機>/bugsink/;備案 8443 只經 443(備案時多一個 8443)
診斷包匯出 Bugsink 讀取 API docker 內網直連,帶 install.sh 建好的 token 不經

陷阱:DSN 有兩個位址角色

Bugsink 用 BASE_URL 組 DSN,而 BASE_URL 是給瀏覽器看站台用的外部位址(子路徑或 8443)。後端送事件要走內網,不能用這個外部位址繞出去再繞回來。B0 要實測「BASE_URL 設外部位址、後端 DSN 改寫成內網位址」送不送得進去;送不進去就要在後端初始化時另外處理。

§6

端到端流程

裝機時:全自動建好站台並接上後端

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
    participant I as install.sh
    participant DB as guidant-db(Postgres)
    participant BS as guidant-bugsink
    participant ENV as guidant.env
    participant BE as 後端三容器

    I->>I: 讀安裝包內烙好的管理員密碼檔
    I->>DB: 建 bugsink 庫與帳號(D6)
    I->>BS: compose up guidant-bugsink(CREATE_SUPERUSER)
    BS->>DB: 自動遷移資料表、建管理員
    I->>BS: 等 /health/ready 回應
    I->>BS: bugsink-manage shell 建 team/project/API token
    I->>DB: SQL 讀出 project.id 與 sentry_key
    I->>ENV: 組好 DSN 寫入 SENTRY_DSN
    I->>BE: compose up 後端
    BE->>BS: 開始送錯誤事件
圖 1 — 首次安裝時序:先起 Bugsink、建專案、讀出 DSN,最後才起後端

升級(--upgrade)時照產品既有慣例:設定檔不重寫,缺了才補(範例 install.sh 第 2734 行 ensure_classifier_env())。舊客戶第一次升到含 Bugsink 的版本時,走的就是上圖整條;已經有的不動。

執行期:錯誤從哪裡送、誰來讀

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  BR["瀏覽器(已登入)<br/>@sentry/vue tunnel"] -->|"443"| NG["nginx<br/>443 入口"]
  OP["原廠人員"] -->|"443 /bugsink/<br/>(備案 8443)"| NG
  NG -->|"轉送端點"| API
  NG -->|"/bugsink/ 子路徑"| BS
  subgraph BEG["後端三容器"]
    API["api<br/>(含驗登入的轉送端點)"]
    SIO["socketio"]
    WK["worker"]
  end
  API -->|"內網直連<br/>過濾→遮罩→節流"| BS["guidant-bugsink<br/>:8000"]
  SIO -->|"內網直連"| BS
  WK -->|"內網直連"| BS
  BS --> PG[("guidant-db<br/>bugsink 庫")]
  DIAG["診斷包匯出"] -->|"canonical API+token"| BS
圖 2 — 執行期事件流:後端內網直連;瀏覽器只走 443,登入後經後端轉送

§7

現況接入點盤點

元件 座標 現況 本案動作
後端初始化 core/plugins/api_log.py:234 讀 SENTRY_DSN 交 jedi-log _extra_before_send 加業務錯誤過濾(棒 A)
jedi-log jedi_api_log/error_tracking/__init__.py(1.3.3) LoggingIntegration 用預設,error log 變事件 多一個參數餵 event_level,發 1.3.4(棒 A)
例外家族 jedi_common/handler/exception.py ClientError 八種 只讀,作為過濾判斷依據
出貨包 scripts/build/build_bundle.sh 第 98–117 行 postgres/seaweedfs 第三方 image 釘 digest 照抄多包一顆 bugsink(棒 B)
stack docker/production/docker-compose.yml 第 457 行 只開 80/443 加 guidant-bugsink 服務;nginx 加 /bugsink/ 子路徑(備案才開 8443)(棒 B)
安裝器 scripts/installer/install.sh 產 .env(compose 用)與 guidant.env(後端用);升級缺了才補 首次建站、升級補站(棒 B)
build 期烙印 scripts/gen_version_bake.py CI 產常數檔、不入版控 管理員密碼檔照這個模式(棒 B)
前端 FE src/main.js @sentry/vue 已拆 復原,tunnel 指向登入後轉送端點,DSN 從登入後端點取(棒 C)
轉送端點 後端新增 無 登入後才能打,原樣轉給內網 Bugsink;只收自家專案的事件,套節流(棒 C)
守衛測試 test/security/code/test_version_module_no_error_tracking_endpoints.py 擋免登入端點復活 保留不動,棒 C 必須通過它
診斷包 infra/support/diag_event_fetcher.py 打 GlitchTip 式 API 改打 canonical API(棒 D)
文件 FR-132-STATE.md 第 19 行、release note v1.26.0 §2b 寫 restart 改 start(棒 B 順手)

§8

拆分表

棒 做什麼 repo 依賴 完成怎麼判定(決策者檢查法)
A 過濾開關 ① 新增 ERROR_TRACKING_CAPTURE_CLIENT_ERRORS(預設 false:八種 ClientError 不送),在後端 _extra_before_send 從 hint 取例外類別判斷;② 新增 ERROR_TRACKING_LOG_EVENTS(預設 false:logger.error 只當 breadcrumb,即附在事件上的前情紀錄,不另開一條),jedi-log init_error_tracking 多一個參數餵 LoggingIntegration(event_level=...),發 1.3.4;後端釘新版;新增前先 grep 確認沒有同義變數;.env.sample 與 deployment-env.md 同步補一行 jedi-log → BE 無 打錯密碼三次站台零條;故意 500 一次站台一條;把兩個變數改成 true 重來,打錯密碼又會出現
B0 Bugsink 實跑 DEV 起 bugsink:2.6.1+stack 的 Postgres(bugsink 庫+獨立帳號),實測清單九項:① shell 建 team/project/token ② SQL 讀 DSN ③ sentry-sdk 2.71 送進去 ④ canonical API 讀回 issue ⑤ 重啟容器後資料仍在 ⑥ 443 子路徑:BASE_URL=https://<host>/bugsink 下網頁可登入、各頁連結與靜態檔不跑掉 ⑦ 轉送:SDK tunnel 經一支假的登入後端點轉給內網 Bugsink,站台收到事件 ⑧ 內外位址:DSN 改內網位址後後端仍送得進去 ⑨ 主機名:升級模式(設定檔已存在)下 install.sh 從哪讀得到主機名;裝完改主機名後 DSN 與站台各會怎樣。產出可直接進 install.sh 的 shell 片段,並給出子路徑通/不通的結論 BE(scripts 草稿) 無 實測紀錄九項皆附實際輸出;第 ⑥ 項明寫「通」或「不通,退 8443」;照片段在空白環境重跑一次能重現
B 進 stack compose 加 guidant-bugsink(healthcheck、環境變數);nginx 加 /bugsink/ 子路徑(B0 第 ⑥ 項不通才改開 8443);guidant-db 建 bugsink 庫與帳號;bundle 多包一顆釘 digest;install.sh 首次建管理員/專案、寫 DSN;--upgrade 缺了才補;build 期烙印管理員密碼檔;guidantai 子命令(bugsink-vacuum、改密碼);修 FR-132-STATE 與 release note 的 restart→start BE B0 新包乾淨機器裝好,https://<主機>/bugsink/(或備案 8443)管理員登入看得到 guidant-ai 專案;故意 500 一分鐘內出現;舊版升級上來同樣成立且產品資料完好
C 前端接回 @sentry/vue 復原,設 tunnel 指向後端新增的轉送端點(登入後才能打、原樣轉給內網 Bugsink、只收自家專案事件、套節流);DSN 由登入後既有端點回應多帶一欄;release 帶版號+commit;trace 標頭串前後端 FE+BE B tunnel 經登入後端點轉送,站台收到帶 release 與 request_id 的前端事件;未登入打該端點回 401;守衛測試仍綠
D 診斷包 diag_event_fetcher.py 改打 canonical API;token 讀 install.sh 建好的那把 BE B0 匯出的診斷包內錯誤事件與站台一致;站台關掉時包內註明未取得而非整包失敗
E 文件 .env.sample、deployment-env.md、客戶手冊(站台入口、密碼存放位置、保留規則、改設定用 start)、release note BE A–D 手冊四件事都找得到;release note 寫明站台入口(子路徑或 8443)
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  A["A 過濾開關<br/>jedi-log→BE"] --> E["E 文件"]
  B0["B0 Bugsink 實跑"] --> B["B 進出貨包"]
  B0 --> D["D 診斷包改 API"]
  B --> C["C 前端接回<br/>(登入後轉送)"]
  B --> E
  C --> E
  D --> E
圖 3 — 棒次依賴

§9

風險與陷阱

🔴 改了 guidant.env 用 restart 不會生效

guidantai restart 只重啟容器不重讀設定檔。install.sh 寫完 DSN 後必須走 up -d;手冊與 release note 一律寫 guidantai start。這條沒寫清楚,客戶會以為站台壞了。

容器重啟會丟掉還沒處理的事件

snappea 佇列寫死在容器內 /tmp。升級或重啟的那幾秒內進來的事件會遺失。UAT 情境可接受,但手冊要寫,免得被當成漏收。

CREATE_SUPERUSER 只在零使用者時生效

升級或第二次跑安裝器時它不會改密碼。所以「改密碼」必須另做維運子命令(棒 B),不能靠改環境變數重跑。

image 內建健康檢查寫死 8000 埠

容器內監聽埠要維持 8000,對外入口(子路徑或 8443)一律由 nginx 轉。改容器內埠會讓 image 自帶的 HEALTHCHECK 永遠失敗。

主機名變了,站台入口與 DSN 會悄悄失效

BASE_URL 含客戶主機名。客戶裝完後改主機名,站台連結會指錯;若 DSN 也是由它組出來,事件送不進去且後端不報錯。B0 第 ⑨ 項要實測影響範圍;手冊要寫「改主機名後跑哪支指令重產」。

升級模式讀不讀得到主機名,尚未實證

舊客戶第一次升到含 Bugsink 的版本時,install.sh 走「設定檔已存在、缺了才補」,不會再問主機名。要從既有設定的哪一欄讀到它,B0 第 ⑨ 項要查清楚;讀不到就要另訂來源,不能在升級時停下來問裝機人員。

轉送端點不可變成開放轉發器

後端轉送端點登入後就能打,若原樣轉發任何內容,等於讓已登入使用者能往內網 Bugsink 灌任意資料。端點只接受自家專案 DSN 的事件、大小設上限、套節流。

過濾開關關掉後,403 類問題在站台上看不到

預設不收業務錯誤,代表「權限設定錯導致一直 403」這種問題要回頭看存取紀錄或臨時把開關打開。這是刻意取捨,手冊要寫開關怎麼開。


§10

與其他需求的關係

FR 關係
FR-071 前作:錯誤追蹤接線、遮罩、診斷包都在那裡建立;本案把收的站換成 Bugsink、改接診斷包 API,前端 release/trace 做法沿用其 design.md
FR-132 v1.26.0 上版文件裡的 restart 寫法由棒 B 順手修正
FR-126 資安總報告 前端免登入端點(U3-⑤)已拆,棒 C 必須在守衛測試下接回