NET6 Core distributed tracing
分散式追蹤系統
系統架構從單體轉變成微服務之後,使用者發出的單次請求往往會涉及到多個服務之間的呼叫,以往採用的日誌服務也較難以窺見全貌,當我們需要追蹤某一次請求中間發生了那些事情,我們期望能獲取的資訊不外乎是這次的請求,總共經過哪些服務,每個服務花費了多久的時間,呼叫的順序等等,這就是分散式追蹤系統能夠幫我們做到的事情。只不過每一家系統的 API 都不太一樣,所以 W3C 也有提供一份Trace Context - W3C,讓大家都用同一個標準來實現分散式追蹤。
在這之前看到一堆像是 RequestId, TraceId, SpanId, ParentId, ActionId,可能還有很多我沒有列出來的名詞,真的是有看沒有懂,現在至少可以從 W3C 的建議中明確知道一些名詞與他們的用途,其他的應該就是各家廠商自己的定義了,那就有用到再說囉
說明
依據 Trace Context - W3C。這份建議提供了一個標準的定義,讓各家廠商可以遵循,使得各家的追蹤系統可以不在各自為政。所以在version為00的情況下,traceparent的格式就是下面這四個東西的組成
- version
- trace-id
- parent-id/span-id
- trace-flags
所以整個概念就像是下面這張圖一樣

這條呼叫鍊上,則是共用同一個 traceId,每一個應用程式自己的 scope 就是用相同的 spanId,在這條呼叫鍊的下一個應用程式則會是另外一個 spanId,前一個應用程式需要將 traceparent、tracestate 這兩個 header 資訊正確的傳遞給下一個應用程式。
事前準備
日誌服務:Seq
1 | # create volume folder |
分佈式追踪系統:zipkin
直接使用下列指令可以透過 docker 執行 zipkin,資料則是暫時儲存於記憶體中不保留,若需要保留資料,zipkin也支援elasticSearch、mySql、cassandra
1 | docker run -d -p 9411:9411 openzipkin/zipkin-slim:2 |
測試專案
測試目標
確認當前 .NET6 Web 網站是否有遵循 W3C 的 traceparent context
測試情境
測試環境由三個應用程式構成
Web(clientApp) -> Proxy(FrontEndApp) -> WebAPI(BackEndApp)
範例程式碼:Github:distributed-tracing-demo
使用者透過瀏覽器開啟網頁,Web 從後端呼叫 Proxy,而 Proxy 則轉發請求給 WebAPI 查詢取得資料後回應給使用者
測試步驟
開啟 clientApp 之後,透過中斷點檢視 FrontEndApp 跟 BackEndApp 的 request Header
frontEnd Header

backEnd Header

可以注意到 header 裡面的 keyValuePair 有一組是traceparent , 00-6ef05abe949ce579cc110ab5b289df14-be6d36f4ecc27214-00
traceparent的格式為version "-" version-format,而version-format在version為00的定義,又由三個部分組成trace-id "-" parent-id "-" trace-flags
實作分佈式跟踪
實作日誌紀錄
接下來的就是要將這些資訊放到日誌服務 Seq 之內,此處選擇使用 Serilog 套件
1 | # dotnet Core Serilog 套件 |
1 | // program.cs |
確認 Seq 是否有紀錄到正確資訊
開啟 frontEndApp 跟 backendApp,並透過中斷點檢視 backendApp所接收到的 traceparent
接著到 seq 查看是否有紀錄TraceId及SpanId

使用 zipkin 檢視 tracing 紀錄
安裝 nuget 套件(照著套件名稱搜尋,並勾選 prerelease)
1 | <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.2.0-rc5" /> |
將應用程式的資料直接透過 exporter 傳遞給 zipkin
1 | // program.cs |
appsetting.json裡面的ServiceName是提供給 zipkin 顯示的服務名稱
1 | //appsetting.json |
