1. 介紹
在現代的服務導向架構 (SOA)、微服務和雲端解決方案中,一個使用者請求可能會「飛過」數十個不同的服務才回傳結果。每個節點都會寫各自的日誌,但怎麼知道某處的錯誤或延遲真的和這個特定請求有關?
分散式追蹤 就像物流裡的包裹追蹤系統:你可以看到包裹經過哪些站點、在哪裡被耽擱、哪裡發生了意外。在程式世界裡——它讓你追蹤一個請求穿越所有服務的「旅程」,了解每個地方花了多少時間,以及哪裡發生錯誤或延遲。
為什麼需要追蹤?
- 定位錯誤:如果出問題,你能立刻看到是哪裡:哪個服務、哪個階段,甚至是哪個具體的方法被呼叫。
- 找出「瓶頸」:追蹤會顯示哪些服務變慢了、在哪個階段損失最多時間。
- 用戶行為分析:可以了解客戶真實如何使用你的服務,哪些地方值得優先優化。
它如何工作?
每個請求會得到一個唯一的追蹤識別碼(TraceId),這個 id 隨請求在整個服務堆疊中「旅行」:從前端到後端、從 API 到資料庫再回來。每個階段會建立「spans」,記錄操作的執行時間和額外事件。最終你會得到一棵(或一個圖)操作的樹狀結構,包含各自的持續時間和相互關係。
分散式追蹤的關鍵實體與術語
| 術語 | 說明 |
|---|---|
| 追蹤 (Trace) | 一個請求穿越多個服務的邏輯路徑。由 TraceId 唯一標識。 |
| Span | 在追蹤中的一個操作或子操作。每個 span 有自己唯一的識別碼。 |
| TraceId | 整個追蹤(整個請求)的唯一識別碼。 |
| SpanId | 當前 span(操作)的唯一識別碼。 |
| Parent SpanId | 父 span 的 Id(如果這個 span 是另一個操作的一部分)。 |
| Attributes | span 的一組自訂元資料:方法名稱、參數、狀態等。 |
| Events/Logs | 與 span 關聯的重要事件(例如錯誤、完成)。 |
| Context Propagation | 在服務與執行緒之間傳遞追蹤資訊的機制。 |
2. OpenTelemetry:觀測性的開放標準
簡介 OpenTelemetry
OpenTelemetry 是一套用來從各種應用收集遙測(追蹤、指標、日誌)的開放標準與工具集合。它由 Cloud Native Computing Foundation (CNCF) 支持,已成為雲端與分散式應用領域的事實標準。
OpenTelemetry(簡稱 OTel)能做到:
- 自動收集並送出追蹤、指標和日誌;
- 支援主流程式語言,包括 C#, Java, Python 等;
- 將資料回傳到觀測系統——Jaeger、Zipkin、Azure Monitor、Grafana 等;
- 透過外掛擴展與設定,適配任何技術棧。
為什麼選 OpenTelemetry?
- 廠商中立:OTel 是標準,而不是某家公司產品。
- 相容性:支援主要的追蹤格式,容易整合到 APM 系統。
- 自動化:大部分工作(例如 HTTP 請求的追蹤)可以自動完成,設定成本低。
- 可擴展性:OTel 在小系統和大系統都表現良好。
OpenTelemetry 架構:簡單解釋
為了不被「層」搞糊塗,我們來視覺化 OTel 中典型的追蹤資料流。
flowchart LR
subgraph 應用程式
A[OpenTelemetry SDK]
end
A -->|追蹤, 指標, 日誌| B[OpenTelemetry Collector]
B -->|匯出到| C[Jaeger/Zipkin/Grafana/Azure/Application Insights 等]
- OpenTelemetry SDK:直接嵌入你的應用程式(例如透過 .NET 的 NuGet 套件)。
- OTel Collector:獨立的集中器服務(常以 Docker container 部署),接收所有應用的 trace 資料並匯出到你指定的目標。
- 觀測前端:你看到漂亮圖表、樹狀圖、能過濾與分析追蹤的系統。
3. 在 C#/.NET 上快速啟動追蹤
我們來在教學用的應用加入基礎的分散式追蹤。主要步驟其實很簡單。
安裝 NuGet 套件
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.Console
OpenTelemetry — SDK 的基底實作;
OpenTelemetry.Exporter.Console — 將追蹤匯出到主控台(也可以換成 Jaeger、Zipkin 等)。
最小化的追蹤設定
在檔案 Program.cs(針對主控台應用):
using OpenTelemetry;
using OpenTelemetry.Trace;
class Program
{
static void Main(string[] args)
{
// 設定追蹤
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource("DemoApp") // 指定來源名稱
.AddConsoleExporter() // 將追蹤匯出到主控台
.Build();
var source = new System.Diagnostics.ActivitySource("DemoApp");
// 開始一個新的追蹤(根 span)
using (var activity = source.StartActivity("主操作"))
{
DoWork();
}
}
static void DoWork()
{
// 一些應用邏輯...
System.Threading.Thread.Sleep(300);
}
}
這裡發生了什麼?
- 建立 tracer provider,註冊來源("DemoApp")並加入主控台匯出器 AddConsoleExporter()。
- 透過 ActivitySource 在程式根部啟動一個新的「activity」(span)。
- 在內部執行要被追蹤的工作。
結果:
Activity.Id: 0b69e3e97ca5f14d
Activity.Operation: 主操作
Duration: 00:00:00.3001082
...
自動化的 HTTP 與資料庫追蹤
OpenTelemetry 支援 instrumentation —— 對常用函式庫做自動追蹤。像是經由 HttpClient 的呼叫和對資料庫(ADO.NET)的查詢可以在不改你業務碼的情況下被收集。
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddHttpClientInstrumentation() // HTTP 呼叫
.AddSqlClientInstrumentation() // SQL 呼叫(如果你有使用)
.AddConsoleExporter()
.Build();
現在透過 HttpClient 的所有呼叫和 SQL 操作會自動出現在追蹤中。
4. 追蹤的視覺化
在主控台輸出只是第一步。要真正發揮價值,還是要用前端 UI 來視覺化。
Jaeger
Jaeger 是最受歡迎的開源追蹤視覺化系統之一。
- 部署 Jaeger(例如用 Docker)。
- 把 AddConsoleExporter() 換成:
.AddJaegerExporter(options => { options.AgentHost = "localhost"; options.AgentPort = 6831; }) - 現在所有的追蹤會在 Jaeger UI 顯示——可以用 TraceId 過濾,查看詳細的時間線圖。
完整指南: OpenTelemetry Jaeger Exporter for .NET
5. 有用的細節
方法比較
不使用 OTel 的話你會手動在日誌裡拷貝 TraceId,希望不會忘記把它傳下去,然後在事件分析時痛苦不已。有了 OTel,整個鏈條會自動建立,並能一鍵視覺化。
| 方法 | 工作量 | 可靠性 | 可擴展性 | 相容性 |
|---|---|---|---|---|
| 手動在日誌中記錄 TraceId | 高 | 低 | 需要持續維護 | 只限你自己的應用 |
| OpenTelemetry | 部署後低 | 有保障 | 廣泛(任意語言、技術棧) | 與大多數 APM 與日誌系統相容 |
架構範例
[Web 客戶端] --> [API 服務] --> [授權服務] --> [資料庫服務]
當使用者按下按鈕,請求會通過這些服務。用 OTel 你可以把「線」(TraceId)在每個服務間延續,自動把行為合併成一個追蹤。
- 在每個服務中 OTel SDK 會自動從 HTTP 標頭辨識 TraceId,並繼續這個鏈路。
- 結果是你可以打開單一追蹤——看到哪些毫秒被花在哪裡、發生了什麼、哪裡出錯。
在真實專案與面試中的應用
- 微服務系統與分散式應用(金融科技、電商平台、SaaS 等)。
- DevOps / SRE:快速定位問題。
- 提升 SLA 與系統響應性。
- 進行效能分析與優化。
- 在面試中展示成熟的工程實踐經驗。
6. 常見錯誤與實作注意事項
忘了傳遞 TraceContext。 如果服務之間沒有傳遞 TraceId(例如忘了把相應的 HTTP 標頭帶上),追蹤會被切斷——呈現成孤立的「點」,觀測的效果就沒了。
未被 instrument 的程式碼。 如果某個函式庫或框架不支援自動 instrument,部分操作會掉出追蹤(但你可以用 ActivitySource 與 StartActivity() 手動建立 spans)。
資料過剩。 對每個請求都收集追蹤在大流量生產環境中成本很高。通常會只對部分請求開啟(sample rate),或基於觸發器收集(錯誤、慢請求)。
效能影響。 使用非同步匯出時 OTel 對效能的影響很小,但在超大流量下仍要注意 overhead,並調整採樣率。
GO TO FULL VERSION