已完成修正(T-1 BE log 轉發)

commit 04e97e1a(branch feature/FR-068尚未 push

白話說明

這棒做的是「讓系統的 log 可以自動送一份到客戶自己的 log 伺服器」。客戶(尤其是把系統裝在自己機房的落地版客戶)通常已經有一台集中管理 log 的機器(rsyslog / Graylog / ELK 這幾種),他們希望我們系統的紀錄也能流進去,這樣所有系統的紀錄在同一個地方查。

現在的成果是:系統管理員在後台填一組「要送到哪台機器、用哪種格式」的設定,按儲存之後不用重開服務就會開始送。可以分開勾選要送「一般應用紀錄」(工程排錯用)還是「稽核事件」(誰登入、誰改了什麼——這是合規客戶接 SIEM 最在意的),或兩種都送。旁邊有個「發送測試訊息」按鈕可以先驗連通再正式打開。

這一棒是純後端,畫面還沒有(那是 T-2 的事);後端 API 已經備好給前端接。

具體做了三件事

  1. 資料表:新增 config.log_forwarding_settings(存設定的表),只套 DEV,STG/POC 沒動。同時新增兩個權限點 log-forwarding.read / log-forwarding.update,授權對象比照現有「郵件伺服器設定」那頁的持有角色(不寫死角色編號,避免各環境授錯人)。
  2. 三支 API
  1. 轉發機制本身:紀錄先丟進一個記憶體佇列,由背景執行緒負責真正的網路傳送——主線程零阻塞,log 伺服器再慢或直接掛掉都不會拖慢 API。

卡片上兩個「查證後定」的點,結論如下

① GELF 要不要引套件 → 決定自己寫(約 130 行)

查證發現候選的兩個套件(pygelfgraypy都不在我們專案的依賴裡,引進來等於多一個第三方依賴。而 GELF 這個格式本身很小(就是一包固定欄位的 JSON,UDP 走壓縮、TCP 用特殊字元分隔訊息),自己寫的成本比「多一個依賴要進打包清單、進離線安裝包、進授權盤點」還低。另一個更實際的理由是:那兩個套件在送不出去的時候會自己重試或把錯誤往上丟,而我們要的是「悄悄降級不要吵」,用套件反而要再包一層去壓制它原本的行為。

② 熱生效怎麼做 → 決定用「每個 worker 自己定期重讀設定」,不是跨 worker 訊號

原本卡片想的是「有沒有現成的跨 worker 通知機制可以借用」。查證結果是完全沒有——整個程式碼庫找不到任何訊號處理、找不到 Redis 訂閱機制(Redis 目前只當單純的快取在用)、也沒有 worker 名冊。gunicorn 內建的重載機制是「整個 worker 重啟」,為了改一個 log 位址而重啟 worker 代價太大。

所以照設計文件寫的「沒有就 fallback,不要為此蓋新輪子」,但 fallback 沒做到「等 worker 自然重生才生效」那麼差:改成每個 worker 各自起一條背景執行緒,每 30 秒重讀一次設定,發現有變才重新掛載。實際效果仍然是「不用重啟服務」,代價只是最多 30 秒的生效延遲,而且 API 回應會誠實地把這個延遲告訴前端(applies_within_seconds 欄位),不假裝是即時的。成本很低——以預設 4 個 worker 算,大約每秒 0.13 次查詢。

驗證方式(本機起假的 log 接收端實跑,不是只看程式碼)