昨天決定了要看什麼(SLI 和 SLO),今天講這些資料從哪裡來。
在 Google 上搜「可觀測性」大概三分鐘就會遇到一個詞:三大支柱(three pillars of observability),指的是 metrics、logs、traces 這三種遙測資料。這個說法一般認為出自 Cindy Sridharan 在 2018 年的《Distributed Systems Observability》,之後被整個業界沿用。
這篇想做的事是把這三種資料分別介紹清楚——它們各自能回答什麼問題、答不出什麼問題——最後再談一個我覺得新手該知道的事:為什麼有些人認為「支柱」這個比喻不太恰當。
在這裡舉例一個場景。
使用者回報:結帳很慢。
這句話裡沒有任何可以行動的資訊。多慢?所有人都慢還是只有他?從什麼時候開始?慢在哪一段?下面三種資料各自能回答其中一部分。
metrics(指標)是隨時間累積的數值。 一筆 metric 大致由四個部分組成:
http_request_duration_seconds(HTTP 請求耗時,單位秒)service="checkout"、method="POST"。標籤是用來分類的鍵值對,讓你之後可以只看某個服務、某種方法同一個名字加上同一組標籤,隨時間累積出來的一串數值,叫做一條時間序列(time series)。
它最重要的特性是便宜。因為存的是聚合過的數字,不管這一秒收到 10 個請求還是 10 萬個請求,那個時間點的資料量幾乎一樣大。便宜到可以每 15 秒收一次、保存好幾個月。
回到「結帳很慢」,用 metrics 可以問出這些:
P95 是「第 95 個百分位數」:把所有請求的耗時從小排到大,排在 95% 位置的那個數字。
P95 是 3 秒的意思是「95% 的請求比 3 秒快,最慢的那 5% 比 3 秒慢」。
昨天講過平均值會騙人,百分位數就是為了避開那個問題。
三張圖就把「很慢」變成「下午 2 點開始,結帳的 P95 從 200 毫秒惡化到 3 秒,錯誤率沒變」。這已經是一句可以交接給別人的描述了。
但 metrics 答不出下一個問題:為什麼。因為它存的是聚合結果,你沒辦法從一條曲線回頭找出「是哪一筆請求出事」。
還有一個限制值得先知道:標籤不能亂加。每多一組不同的標籤值就多一條時間序列,如果把「使用者 ID」當標籤,一百萬個使用者就是一百萬條序列,Prometheus 會被打爆。這個問題叫 cardinality(基數)爆炸,之後會專門講。
logs(日誌)是一筆一筆帶時間戳的事件紀錄。 跟 metrics 相反,它保留細節:哪個使用者、帶了什麼參數、錯誤訊息全文、程式在哪一行出錯。
接著剛才的場景:把時間範圍縮到下午 2 點前後,去翻結帳服務的 log,看到大量這樣的紀錄——
connection pool exhausted, waiting for available connection
(連線池用盡,正在等待可用連線)
真相浮現了。
連線池:程式跟資料庫連線很花時間,所以會事先建好一批連線重複使用,這批連線就叫連線池。
池子裡的連線被佔滿時,後來的請求只能排隊等。
所以請求是在排隊,等到了就成功,只是很慢——這正好解釋了為什麼錯誤率沒有上升。metrics 告訴我們「慢但沒壞」,log 告訴我們「因為在排隊」。
log 的代價是量跟可搜尋性。一個中等流量的服務一天就能產生幾十 GB。更麻煩的是格式:如果 log 長這樣——
2026-08-27 14:03:22 checkout failed for user 8821 after 3021ms
——你沒辦法問「過去一小時裡超過 3 秒的請求有幾筆」。因為那個 3021 是黏在一句英文裡的字串,不是一個可以拿來比大小的欄位。你只能用 grep(在文字裡搜關鍵字的指令)土法煉鋼。
這就是之後會提到的結構化日誌:把 log 寫成有欄位的格式(例如 JSON),才能被查詢,而不只是被搜尋。
前面兩步知道了「連線池被佔滿」,但還有一個問題沒答:是誰把連線池吃光的?
現代的系統常常是微服務架構——把一個大程式拆成好幾個小程式,各管一件事,彼此透過網路互相呼叫。所以一筆結帳請求可能跨了五個服務。這時候 log 各自躺在五個地方、時間戳交錯在一起,人腦拼不回來。
traces(追蹤)就是為了這件事存在的。 它把一個請求在整個系統裡走過的完整路徑串成一棵樹:
於是畫面上會看到類似這樣的東西:整個請求 3.1 秒,其中「呼叫優惠券服務」這個 span 佔了 2.9 秒,而優惠券服務裡面有 2.8 秒都花在等一個資料庫查詢。
真兇是優惠券服務的慢查詢,它把上游結帳服務的連線池佔住了。
這個因果關係,metrics 看不到(它只知道結帳慢),logs 也看不到(它只知道結帳這邊沒連線可用)。只有 trace 看得到。
這也解釋了為什麼分散式追蹤是這幾年才紅起來的:單一程式的年代,一個請求從頭到尾都在同一支程式裡跑完,出錯時看堆疊訊息就夠了;一旦拆成十幾個服務,「誰拖累誰」就變成一個沒有工具就答不出來的問題。
| 存的是 | 回答的問題 | 成本 | 罩門 | |
|---|---|---|---|---|
| Metrics | 聚合後的數值 | 有沒有問題?多嚴重?何時開始? | 低 | 答不出「是哪一筆」 |
| Logs | 一筆筆事件 | 那一刻發生了什麼? | 高 | 難聚合、跨服務拼不起來 |
| Traces | 請求的完整路徑 | 慢在哪一段?誰呼叫誰? | 中(通常要抽樣) | 要改程式碼埋點 |
上面那個場景的順序也不是隨便排的。實務上的除錯流程通常就是:
metrics 發現 → logs 或 traces 定位 → 修
從最便宜的資料開始,縮小範圍之後再去翻貴的資料。
介紹完三者,講一個我覺得新手值得先知道的討論。
「三大支柱」這個說法在社群裡一直有人反對,理由是柱子聽起來像三根各自獨立的東西,容易讓人以為裝三套工具、蓋三座資料倉庫就完事了。持這個看法的人(Honeycomb 的 Charity Majors 是最常被引用的一位)主張,可觀測性的重點是能不能對系統提出任意問題,而不是收集了幾種資料。
我不夠格評判這個爭論,但上面那個場景確實支持他們的說法——每一次轉場都建立在「能不能跳過去」:
如果不能,你的流程會變成:在 Grafana 看到尖峰 → 手動記下時間 → 切到另一個分頁打開 Loki → 手動輸入時間範圍 → 肉眼掃 log → 再切一個分頁打開 trace 系統重找一次。每切換一次就流失一次上下文,而且慢到讓人放棄。
所以我給自己一個判準:從一張圖上的異常點出發,要幾次點擊才能看到造成它的那筆 log?如果答案是「開三個瀏覽器分頁」,那我有的是三個孤島,不是三大支柱。
有人主張加上 profiling(效能剖析),它回答的是「CPU 或記憶體到底花在哪一行程式碼」。也有人把 events(事件)或 dumps 算進去。
這個系列不做 profiling,昨天說過理由。但知道它存在有意義:哪天 trace 告訴你「就是這個 span 慢」,你卻答不出「這個 span 裡面哪一行慢」,那就是該去找 profiling 的時候。
順序是刻意的:從最便宜、最容易上手的 metrics 開始,最後才做 traces——它需要改程式碼,門檻最高。
而且 Day 6 會埋進去的三個故障,是配著這個順序設計的:第一個只有 log 查得到、第二個只有 trace 查得到、第三個要靠告警才攔得下來。
明天 Day 4 收掉「為什麼」這個區塊:列出這 30 天要裝的東西、每個為什麼選它,以及我刻意不做的事。