iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Build on Google AI

從 Vibe Coding 到 Production:用 Google AI 打造上線守門員系列 第 12

Day 12|服務掛掉以前看得見嗎?Logs、Metrics 與 Traces 入門

  • 分享至 

  • xImage
  •  

Day 11,我們盤點了應用程式安裝的套件與已知弱點。

但產品進入正式環境後,新的問題才剛開始。外部服務可能變慢,某個 Endpoint 可能持續回傳 500,使用者也可能在團隊發現以前就已經無法結帳。

如果系統只在 Terminal 印出一句:

Something went wrong

工程師不知道問題影響多少人,也不知道錯誤從哪個服務開始。

今天會建立一個本機 Checkout API,並同時產生三種可觀測性訊號:

  • Logs:記錄單次事件。
  • Metrics:衡量整體趨勢。
  • Traces:追蹤一次請求經過哪些工作。

我們還會模擬付款服務逾時,觀察三種訊號各自回答什麼問題。

Monitoring 與 Observability 有什麼差別?

Monitoring 會收集與顯示已知指標。例如:

  • 每分鐘有多少請求?
  • 錯誤率是否超過 5%?
  • P95 Latency 是否高於 800 毫秒?

Observability 則關心我們能否從系統輸出的訊號,理解內部發生什麼事。

當 Checkout 錯誤率升高時,我們還要繼續回答:

  • 哪一類請求失敗?
  • 哪個外部依賴變慢?
  • 問題影響所有使用者,還是單一區域?
  • 同一次請求在哪個步驟耗費最多時間?

Monitoring 告訴我們「發生問題」。Observability 協助我們追查「問題為什麼發生」。

兩者不是互斥選項。團隊需要先監控使用者可見的症狀,再使用 Logs 與 Traces 找出原因。

三種訊號各自回答什麼?

訊號 適合回答 不適合單獨回答
Logs 某次請求發生什麼事件?錯誤代碼是什麼? 整體錯誤率是否持續升高?
Metrics 流量、錯誤率與延遲如何變化? 某次失敗經過哪些服務?
Traces 單次請求在哪個步驟耗時或失敗? 一個月內總共有多少次錯誤?

只保存 Logs,團隊可能要掃描大量文字才能知道錯誤是否普遍。

只保存 Metrics,團隊會知道錯誤率上升,卻不知道是哪個依賴造成。

只保存 Traces,成本與資料量可能快速增加,也不適合計算所有長期趨勢。

三種訊號透過相同的 Route、Trace ID、錯誤類型與時間範圍互相連結,才能縮短排查時間。

建立 Day 12 的本機情境

今天的程式放在:

demo-app/observability-demo/scenario.js

腳本會啟動一個本機 HTTP Server,並送出四個請求:

  1. 一次成功的 Health Check。
  2. 兩次成功的 Checkout。
  3. 一次付款服務逾時的 Checkout。

進入 Demo:

cd /media/mickey/777/ithome/demo-app

執行:

npm run observability:demo

這個實驗不會連接真正的付款服務。程式只用延遲模擬成功與 Timeout。

Logs:保存單次事件的上下文

每個 HTTP Request 完成後,Demo 會寫入一筆 JSON:

{
  severity,
  event: "http.request.completed",
  requestId,
  traceId,
  route,
  status,
  durationMs,
  errorType
}

成功的 Checkout Log 類似:

{
  "severity": "INFO",
  "event": "http.request.completed",
  "requestId": "checkout-success-1",
  "traceId": "<trace-id>",
  "route": "/checkout",
  "status": 200,
  "durationMs": 9
}

付款逾時則產生:

{
  "severity": "ERROR",
  "event": "http.request.completed",
  "requestId": "checkout-timeout-1",
  "traceId": "<trace-id>",
  "route": "/checkout",
  "status": 503,
  "durationMs": 37,
  "errorType": "DEPENDENCY_TIMEOUT"
}

實際時間會依電腦負載改變。重要的不是固定數字,而是欄位與單位保持一致。

為什麼使用 JSON?

以下文字 Log 適合人眼閱讀:

Checkout failed because payment timed out after 37 ms

但查詢工具很難可靠拆出 Route、Status 與 Duration。

JSON 把每項資料放在固定欄位:

{
  "route": "/checkout",
  "status": 503,
  "durationMs": 37,
  "errorType": "DEPENDENCY_TIMEOUT"
}

Cloud Logging 把 JSON Payload 視為 Structured Log。團隊可以查詢指定欄位,也能建立索引與統計。

欄位命名必須穩定。今天叫 durationMs,明天不能隨意改成 elapsed,否則既有查詢與 Dashboard 可能失效。

Log 應該記錄什麼?

一筆 Request Log 通常需要:

  • 時間
  • Severity
  • Event Name
  • Route
  • Status
  • Duration
  • Request ID
  • Trace ID
  • 可分類的 Error Type

Log 不應直接記錄:

  • Access Token
  • Cookie
  • Password
  • 完整 Request Body
  • 不必要的 Email 或個人資料

Day 8 已經證明,完整記錄 Request 會讓 Log 變成另一個資料外洩面。

Request ID 與 Trace ID 有什麼差別?

Request ID 用來識別進入目前服務的一次請求。客服可以把 Request ID 提供給工程師,工程師再查詢對應 Log。

Trace ID 用來連結同一條跨服務流程中的所有 Span。

例如:

Browser
  └── Checkout API
        ├── Inventory Service
        └── Payment Service

三個服務應傳遞同一個 Trace ID。每個服務再建立自己的 Span ID。

如果系統只有單一服務,Request ID 與 Trace ID 看起來可能很相似。但服務拆分後,Trace ID 才能重建完整路徑。

Metrics:判斷問題有多大

Logs 記錄四次請求的個別結果。Metrics 則把結果聚合。

這次執行產生:

http_requests_total{route="/health",status="200"} 1
http_requests_total{route="/checkout",status="200"} 2
http_requests_total{route="/checkout",status="503"} 1

Checkout 共收到三次請求,其中一次失敗:

Checkout error rate = 1 / 3 = 33.3%

這個比例只用來說明計算方式。四次測試請求的樣本太小,不能代表正式環境表現。

程式也計算各 Route 的 Duration:

http_request_duration_ms{route="/health"} count=1 avg=2 max=2
http_request_duration_ms{route="/checkout"} count=3 avg=18 max=37

實際系統通常使用 Histogram 計算 P50、P95 與 P99,而不是只看平均值。

平均值會隱藏少數很慢的請求。例如九次請求需要 100 毫秒,一次需要 5 秒,平均值約為 590 毫秒。多數人很快,但仍有使用者等待 5 秒。

Google SRE 的四個 Golden Signals

Google SRE 建議優先觀察四種訊號:

  1. **Latency:**處理請求需要多久。
  2. **Traffic:**系統承受多少需求。
  3. **Errors:**請求以明確或錯誤結果失敗的比例。
  4. **Saturation:**資源距離上限還有多遠。

今天的 Demo 已涵蓋前三項:

  • http_request_duration_ms 表示 Latency。
  • http_requests_total 表示 Traffic。
  • HTTP 503 與 dependency_failures_total 表示 Errors。

Demo 尚未模擬 Saturation。正式服務還要觀察 CPU、Memory、Connection Pool、Queue Length 與 API Quota 等資源。

Metric Label 不要放入無上限的值

以下 Label 適合聚合:

route="/checkout"
status="503"
dependency="payment"
error_type="timeout"

以下值不適合直接成為 Metric Label:

user_id="每位使用者都不同"
request_id="每次請求都不同"
email="個資且幾乎每人不同"
full_url="/projects/每筆-ID"

這些值會產生大量 Time Series,稱為 High Cardinality。監控成本、記憶體與查詢速度都可能受到影響。

需要查詢單次 Request 時,應使用 Log 或 Trace,不要把 Request ID 放進 Metric Label。

Traces:找出時間花在哪裡

OpenTelemetry 將一次請求經過的路徑稱為 Trace。Trace 由多個 Span 組成。

這次失敗的 Checkout 產生兩個 Span:

{
  "traceId": "<same-trace-id>",
  "spanId": "<payment-span-id>",
  "parentSpanId": "<http-span-id>",
  "name": "payment.authorize",
  "status": "ERROR",
  "errorType": "DEPENDENCY_TIMEOUT",
  "durationMs": 36
}
{
  "traceId": "<same-trace-id>",
  "spanId": "<http-span-id>",
  "parentSpanId": null,
  "name": "HTTP GET /checkout",
  "status": "ERROR",
  "errorType": "DEPENDENCY_TIMEOUT",
  "durationMs": 37
}

兩個 Span 使用相同 Trace ID。payment.authorize 的 Parent Span 是 HTTP Checkout。

從這份 Trace 可以看出:

  • 整次請求約花費 37 毫秒。
  • Payment Span 約花費 36 毫秒。
  • Checkout 不是在本機計算或資料庫步驟失敗。
  • 付款依賴的 Timeout 佔用大部分時間。

Metrics 先告訴我們 Checkout 出現 503。Trace 再把原因縮小到付款依賴。

告警應該針對症狀,還是原因?

假設付款服務 Timeout,Checkout 開始回傳 503。

可以建立兩種告警:

  • 症狀:Checkout 錯誤率超過門檻。
  • 原因:Payment Timeout 次數增加。

Google SRE 建議優先針對使用者可見症狀通知值班人員。原因可能改變,症狀則直接代表使用者受到影響。

付款 Timeout 可以幫助排查,但不一定每次都需要叫醒工程師。如果系統已經成功 Retry,使用者沒有看到錯誤,就不應把每次短暫 Timeout 都升級成重大告警。

一個有效告警要回答:

  1. 哪個使用者功能正在失敗?
  2. 影響是否持續?
  3. 影響是否已超過團隊可以接受的標準?
  4. 收到告警的人是否能採取行動?

「CPU 看起來有點高」通常不是完整告警。

Production Readiness Agent 要檢查哪些證據?

Agent 不能只搜尋程式中是否出現 console.log 或 Monitoring SDK。

它應檢查:

  1. 核心使用者流程是否有成功率與 Latency Metric。
  2. 錯誤是否使用穩定的 Error Code 分類。
  3. Log 是否採用固定欄位與結構化格式。
  4. Log 是否包含 Token、Cookie 或不必要個資。
  5. 跨服務呼叫是否傳遞 Trace Context。
  6. Metric Label 是否包含 User ID、Email 或 Request ID。
  7. 告警是否對應使用者可見症狀與處理手冊。
  8. Dashboard 是否能回答流量、錯誤與延遲。

一項可操作的 Finding 可以寫成:

問題:Checkout 失敗沒有可聚合的錯誤分類
證據:所有例外只輸出自由文字 "Something went wrong"
影響:團隊無法計算各錯誤原因的發生率,也無法建立穩定告警
修正:新增 errorType 與 route 欄位,並記錄 HTTP Status
驗證:模擬 Payment Timeout,確認 Metric 增加且 Log 可用 Trace ID 關聯

Agent 也要區分「缺少證據」與「確認沒有監控」。

Repository 沒有監控設定,不代表正式環境一定沒有監控。設定可能存在另一個 Infrastructure Repository 或雲端平台。Agent 應要求補充 Dashboard、Alert Policy 或部署設定,不能直接宣稱團隊毫無監控。

今天的結論

Logs、Metrics 與 Traces 不是三套互相取代的工具。

Metrics 告訴我們問題有多大。Logs 保存單次事件的上下文。Traces 顯示一次請求經過哪些工作,以及時間花在哪裡。

今天的付款逾時同時留下三種證據:

  • Metric 顯示 Checkout 出現一次 503。
  • Log 保存 Request ID、Trace ID 與 DEPENDENCY_TIMEOUT
  • Trace 顯示 payment.authorize Span 佔用大部分時間並失敗。

明天,我們會處理 Timeout 與 Retry。如果錯誤處理方式不正確,一個短暫的外部故障可能被放大成整個系統的事故。

參考資料


上一篇
Day 11|套件裝得越快,風險來得越快:依賴與供應鏈安全
下一篇
Day 13|錯誤處理、Timeout 與 Retry:避免小故障變成大事故
系列文
從 Vibe Coding 到 Production:用 Google AI 打造上線守門員13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言