iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0

! 本篇文章將會介紹 日誌與稽核:自動化系統的黑盒子,期望大家都能讓自動化系統出事時不用通靈,翻黑盒子就能還原真相 :D

昨天(D24)把進度儀表板做出來之後,這套系統已經會自己出貨、自己回報進度了。但儀表板看的是「現在」,還有一個方向沒交代——往回看。這套管線每晚無人值守地在跑,出事的時候我在睡覺(或者在耍廢,誰叫系列叫這個名字),唯一在場的證人就是日誌。

飛機出事靠黑盒子還原真相,自動化系統也一樣。好消息是這套系統的黑盒子從第一天就在錄了,今天只是把它打開來盤點:錄了什麼、給誰看、以及怎麼用它把昨天剛發生的一場真實事故,在五分鐘內還原成時間軸。

本篇目標

讀完這篇你會學到:

  • 這套系統既有的四層日誌:流水帳、launchd 輸出、狀態檔、證據檔,各記了什麼、給誰看
  • flight recorder 思維:黑盒子不是出事才錄,是平常就一直在錄
  • 事後除錯實錄:不重跑、不靠記憶,翻 log 重建 d24 的 14 分鐘事故

環境準備

延續既有管線,log 相關的東西都已經在硬碟上了:

$ ls logs/ | tail -6
generate-20261004.log   # 生成流水帳(一天一檔)
publish-20261004.log    # 發文流水帳(一天一檔)
launchd-publish.out     # launchd 收集的 stdout(跨日累積)
launchd-publish.err     # launchd 收集的 stderr(腳本炸掉先看這)
launchd-republish.out   # 20:15 補發 job 的輸出
launchd-republish.err

不需要裝任何新工具,用的都是 D10 的 plist 設定(StandardOutPath/StandardErrorPath)和腳本開頭那個小到不像話的 log() 函式。

主要內容

步驟一:日誌分層——四層黑盒子,各回答不同問題

打開 logs/ 和 state/ 盤點一下,這套系統不知不覺已經有四層紀錄,每層回答的問題都不一樣:

  • 流水帳層(logs/generate-*.log、publish-*.log):記「每一步做了什麼」,給人讀的細節
  • 系統層(logs/launchd-*.out、.err):記「你沒想到要 log 的輸出」,腳本在碰到 log() 之前就炸掉時,只有這層接得住
  • 事實層(state/published.log、published-*.flag):記「發生了什麼事實」,一行一事實,機器讀的(D24 的儀表板就是讀這層)
  • 證據層(screenshots/*.png、state/quarantine-*.md):眼見為憑的截圖,加上被機敏掃描攔下的文章隔離區

流水帳層的核心其實只有兩行:

# generate.sh / publish.sh 開頭的日誌核心
LOG="$ROOT/logs/generate-$(date +%Y%m%d).log"
log() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG"; }

四個設計決定:時間戳前綴是時間軸重建的基礎;tee -a 讓手動測試時畫面即時可見、同時 append 進檔案;$(date +%Y%m%d) 一天一檔,輪替機制用檔名就解決了;檔名即索引——查 10 月 4 日的事就看 publish-20261004.log,不用 grep 整個目錄。

步驟二:flight recorder 思維——黑盒子平常就一直在錄

飛機的黑盒子不是出事那一刻才開始錄。落到寫程式,是三個習慣:

動作前先記意圖,動作後記結果:

# publish.sh:點擊前先記「打算做什麼」,失敗時 log 會停在案發前一刻
log "點擊發佈(第 ${attempt}/3 次)"
"${AB[@]}" eval '...click()' >>"$LOG" 2>&1 || log "發佈按鈕 eval click 失敗(將重試)"

子指令輸出一律進同一本帳:>>"$LOG" 2>&1,agent-browser 每一步的 stdout 和 stderr 全部進流水帳,一步都不漏。

事件當下順手記,不要事後補:D24 講過 d01 沒進 published.log 的教訓;隔離區同理——secret_scan 掃到機敏的當下就 mv 進 state/quarantine-dXX-HHMMSS.md 並記一筆 log,事後不用翻對話紀錄就知道當時擋下了什麼。

小小小測驗:你知道如果 plist 沒設 StandardErrorPath,腳本炸掉時那些錯誤訊息會去哪嗎?(答案:直接消失,launchd 預設把 job 輸出丟進黑洞——這也是踩坑記錄第一條值得供起來的原因)

成本順手算一下:ls -lh logs/ 顯示 generate 流水帳一天 2~35KB,30 天總量還不到一張 1MB 的截圖。錄影成本趨近於零,事後除錯的價值無價,這筆帳怎麼算都划算。

步驟三:事後除錯實錄——還原 d24 的 14 分鐘

昨天剛好發生一場真實事故,黑盒子全程錄下來了:

[2026-10-04 20:09:58] FATAL: 找不到 d24 的文章檔,今日無法發文
[2026-10-04 20:17:05] FATAL: 找不到 d24 的文章檔,今日無法發文
[2026-10-04 20:22:54] d24 發文來源:…/articles/2026-10-04-d24.md
[2026-10-04 20:23:19] 發佈成功:https://ithelp.ithome.com.tw/articles/10420900

不用任何猜測,三幕劇直接浮出來:20:09 發文 job 準時開跑 → 文章不存在 → die() 記 FATAL、Discord 告警(D13);20:15 republish job(D23 的重試鏈)接手 → 還是沒有 → 第二次告警;20:22 人類被叫醒後補好文章、手動重跑 publish.sh → 20:23 成功收工。兩次 FATAL 不是系統壞掉,是劇本照演——機器擦了兩次屁股擦不動,把球丟給人,人接住了。稽核的意義就在這:事後你能對任何人(包括一個月後的自己)證明系統每一步都按設計走。

再把鏡頭拉到系統層。launchd-publish.err 裡躺著早期留下的幾行:

scripts/publish.sh:read:88: bad option: -a
scripts/publish.sh:89: TAGARR[@]: parameter not set
發送失敗 (HTTP 000)

bad option: -a 是 zsh 的錯誤格式——bash 的 read -a 在 zsh 要寫 read -A,看到這行就知道某次執行被 zsh 接手了。HTTP 000 則代表 curl 連線根本沒建立(網路層就掛了),不是 server 拒絕。這些錯從來不會出現在 log() 的流水帳裡,因為它們發生在你沒想到要 log 的地方——只有系統層的黑盒子接得住。

常見問題 / 踩坑記錄

  • Q:腳本明明炸了,為什麼什麼錯誤訊息都沒有?
    A:launchd 預設把 job 的 stdout/stderr 丟掉,必須在 plist 明確設定 StandardOutPath/StandardErrorPath(D10 的 plist 已經有)。另外要分清楚兩層:腳本內部的錯會進 log() 的流水帳,但腳本「還沒跑到 log() 就炸」或子程序的錯只會出現在 launchd-*.err。除錯時兩層都要翻。

  • Q:log() 為什麼用 tee -a,不直接 >> 檔案就好?
    A:tee -a 讓同一行輸出餵兩個讀者——終機畫面(手動測試時即時看得到)和 log 檔(append 不覆蓋)。launchd 跑的時候,終機那一份又會被 plist 的 StandardOutPath 收進 .out,等於一條輸出三個去處。後面再接 2>&1 把 stderr 併進來是第三件必做的事,不然錯誤永遠只出現在你看不到的地方。

  • Q:日誌會不會無限長大?機敏值會不會被記進去?
    A:兩件事都靠設計而非自覺。輪替用「一天一檔」的檔名解決,每檔幾十 KB,賽後想清就整批刪。機敏則是雙保險:webhook、token 這種值從頭到尾不進 log 語句——告警只記「已送達 (HTTP 204)」,不印 URL 本體;文章本身還有 secret_scan 加隔離區擋在 log 之前(d09 那天就連擋五次,五個 quarantine-d09-*.md 時間戳從 19:04 排到 19:25,全程可稽核)。原則一句話:log 記「發生什麼事」,不記「開門的鑰匙」。

小結

  • 四層黑盒子各司其職:流水帳記過程、launchd out/err 接住系統層意外、狀態檔記事實、截圖與隔離區是證據
  • flight recorder 思維:動作前記意圖、動作後記結果、子輸出全進帳;錄影成本趨近於零
  • 事後除錯不靠通靈:d24 的 14 分鐘全程可重建——系統照劇本演完、人類準時接手,這就是稽核

明日預告

下一篇我們要介紹 異常自癒:self-healing 的第一步,敬請期待!這幾天的黑盒子與告警都止步於「告訴人類」,明天開始讓系統自己動手——偵測到異常之後,第一步自癒怎麼做。

參考資料:

有任何疑問但沒有 iT 邦幫忙帳號,或是想匿名提問?
歡迎到 https://dev.benben.me/q/Z5442T 提問或加油打氣,沒意外的話會在完賽之後一起回答 :D


上一篇
24 進度追蹤:30 天作戰地圖
下一篇
26 異常自癒:self-healing 的第一步
系列文
自我耍廢組:全自動化の鐵人 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言