cover

給還在用 dd()console.log debug 的你——來聊聊正經的 Log 設計,讓你半夜被叫起來的時候不會想哭。

先講結論

好的 Log 設計要做到一件事:出事的時候能在五分鐘內還原現場。達到這個目標只需要兩步——設計好 Log 格式,然後用 middleware 自動記錄。

系統監控的兩種路線

你的系統掛了,你怎麼知道?

類型做法例子
主動式自己埋 Log,自己看自定義 access log
被動式讓外部工具幫你盯Datadog、GCP Stack Driver

哪個比較好?老實說,兩個都要做。但如果你只能選一個,先做主動式。因為外部工具再厲害,也不知道你的業務邏輯長什麼樣——它能告訴你 response time 變慢了,但不能告訴你是哪個使用者的哪個操作觸發了 bug。

Log 格式怎麼設計?

我用過的格式長這樣:

[{DATETIME}] {APP_ENV}.{LEVEL}: {TYPE} {IP} {TIMESTAMP} {METHOD} {REQUEST_URI} {PAYLOAD} {HEADER} {IDENTIFIER}

看起來很多欄位對吧?但每個都有用:

  • DATETIME + TIMESTAMP:兩個時間?對,一個給人看,一個拿來程式篩選
  • APP_ENV:staging 和 production 的 Log 混在一起你就知道痛了
  • LEVEL:INFO / ERROR / WARNING,查問題的時候直接 grep ERROR
  • TYPE:標記是 Request 還是 Response,這樣你能看到「進去什麼、出來什麼」
  • IDENTIFIER:使用者識別,出事的時候追蹤特定用戶的行為軌跡

掛在 Middleware 上

為什麼用 middleware?因為 Laravel(或 Django)的 middleware 每個 request/response 都會經過。你只要寫一次,所有 API 的 Log 就自動記好了。

不要在每個 Controller 裡面手動寫 Log::info(...)你會忘記的,我保證。

實戰查詢指令

Log 記了不查等於沒記。以下是我最常用的幾個:

追蹤某個使用者的操作歷程:

grep 1682394555 middleware_access-2023-04-25.log | awk '{print $1" "$2" "$3" "$6" - "$7" - "$8}'

用 timestamp 當關鍵字,就能撈出同一時間段的所有 request。

看某支 API 被打了幾次:

grep /api/sale-property/search-meta middleware_access-2023-04-25.log | wc -l

發現某支 API 被打了異常多次?可能是前端寫了無限迴圈,也可能是有人在爬你的資料。

統計錯誤次數:

grep ERROR middleware_access-2023-04-25.log | wc -l

如果數字突然飆高,恭喜,你有事情要處理了。

做完 Log 之後呢?

Log 只是第一步。接下來你會需要:

  • 告警系統:錯誤超過閾值自動通知(不然你得自己盯著看)
  • Log 輪轉:決定 Log 檔案多久清一次,不然硬碟會被塞爆
  • 串接監控平台:把 Log 接到 Grafana 或 ELK,用圖表看趨勢

我在 Proto 專案裡把 Log 分成四層:request log、response log、error log、query log。四層疊起來,任何一次 API 呼叫的完整過程都能還原。聽起來很搞工,但半夜被叫起來 debug 的時候,你會感謝自己。


Log 寫得好不好,決定了你半夜被 call 的時候是花五分鐘還是五小時。

延伸閱讀