在 ASP .NET Core 中使用 OpenTelemetry,為應用程式埋下觀測點
這篇文章主要會說明為什麼選擇使用 OpenTelemetry 作為處理追蹤資料的套件,還有如何利用 .NET System.Diagnostics
函式庫來產生追蹤資料,並交由 OpenTelemetry 來接手處理後續流程的方式。
為什麼選擇 OpenTelemetry
OpenTelemetry 是一個可以採集 trace, metric, log 三樣遙測資料的整合工具,支援多種程式語言,同時又是 CNCF 裡的項目。以往想要導入 APM 的工具時,基本上就只能使用那家工具所開發的套件,日後想要換工具,就會遇到服務內部的程式碼都要改用相對應套件的情況,亦增加了更換工具的成本。現在的話,只要裝 OpenTelemetry 這一套,就可以隨意更換後面的 APM 工具,不會因為更換套件的成本很高而被特定的工具綁住。OpenTelemetry Protocol 目前已經有很多 APM 工具支援它的格式,例如 Grafana、Elastic APM。
當然,OpenTelemetry 也不是說有其他 APM 工具支援就完美無缺的,要看支援程度高不高,有機率出現搭配自家套件才能讓整套工具更好用、有一好沒兩好的情況,這點就要自行評估。Grafana Tempo 對於 OpenTelemetry Protocol 格式的支援度很高,欄位的對應也一致,但是對於用追蹤的資料來製作統計資料,這點就難度很高,甚至沒辦法製作,當然跟我對 Grafana 製作圖表很不熟也有關係。後續也有嘗試使用 Elastic APM 搭配 OpenTelemetry(送到 OTel Collector 再轉到 Elastic Agent),也試過搭配 Elastic 自家的套件 Elastic.Apm(直送 Elastic Agent),結果兩者的資料在 Elastic APM 上發揮的效果,還是自家套件較能完整發揮。
運作流程
整個運作流程也很簡單,.NET 的應用程式裡使用 System.Diagnostics
函式庫產生追蹤資料(手動埋點),然後交由 OpenTelemetry 套件接手處理自動側錄、輸出的目的地等設定。處理完成後,再透過 OpenTelemetry Protocol 來傳到採集器(OpenTelemetry Collector 或是其他採集器),之後採集器再送到 APM 工具內,交由我們查看。
OpenTelemetry 套件
這裡提供一些我常用的套件,GitHub 裡也有很多官方和協作者(contribute)所建立的套件,可依需求選用(裡面地雷還不少,有的會互相衝突)。各套件的使用說明都在 GitHub 裡,沒有額外的網站說明。另外,有一些套件目前還在預覽版,記得要先允許使用預覽版的套件,這樣才能看到。
套件版本可能較舊,若使用新版本,可能用法會不同,建議搭配官方文件。
1 | OpenTelemetry 1.4.0 |
接下來用函式庫的類型來分類,簡單說明一下各套件的用途:
核心
沒了它,接下來的內容可以不用看了。
套件 | 說明 |
---|---|
OpenTelemetry | OpenTelemetry 的核心套件 |
OpenTelemetry.Extensions.Hosting | 提供 tracing 和 metrics 自動開始和停止側錄的擴充方法,簡化 OpenTelemetry SDK 的生命週期 |
Exporter
將資料輸出到指定位置,有很多輸出的位置,也可以同時使用多個,例如:Console, InMemory, Jaeger, OpenTelemetry Protocol 等。
套件 | 說明 |
---|---|
OpenTelemetry.Exporter.Console | 將 System.Diagnostics.Activity 產生的資料輸出到 Console,方便在開發的時候「人工」觀測和解析 |
OpenTelemetry.Exporter.OpenTelemetryProtocol | 將 System.Diagnostics.Activity 產生的資料轉成 OpenTelemetry Protocol 格式並輸出到指定位置,通常是送到 collector 或是 agent。提供 gRPC 和 HTTP 的方式傳送 |
Instrumentation
自動側錄的工具,不同的工具會自動側錄它注重的資料,例如 HTTP 就會注重 HTTP 動詞、status code、url、query string 等,這個就交由各位自行在 Console 或是追蹤資料中觀察。需要注意的是,這些套件和 OpenTelemetry .NET Automatic Instrumentation 不同,這個需要安裝在主機上,然後就能自動側錄主機上的所有服務,基本上不需要修改服務的程式碼,有興趣的可以自行研究(這東東也是個坑)。
套件 | 說明 |
---|---|
OpenTelemetry.Instrumentation.AspNetCore | 自動側錄 ASP .NET Core 專案的資料,需要注意,它只會側錄這個專案,其他的一概不理 |
OpenTelemetry.Instrumentation.Http | 自動側錄 HttpClient 這個物件的資料 |
OpenTelemetry.Instrumentation.SqlClient | 自動側錄 SqlClient 這個物件的資料,同時支援 Microsoft.Data.SqlClient 和 System.Data.SqlClient ,可以側錄 SQL 語句 |
實作
OpenTelemetry 會搭配 .NET 提供的 System.Diagnostics
函式庫來取得追蹤資料,之後 OpenTelemetry 就會依照設定開始介入處理。在實作的時候可以選擇埋點或不埋點,不埋點只要在Program.cs
中調整好配置就能直接使用,可以直接看設定 OpenTelemetry
的章節。想要埋點,看比較詳細的流程,可以看完埋點
。關於 .NET OpenTelemetry 的詳細使用說明就不贅述了,可以參考下面的文件:
- .NET | OpenTelemetry
- 新增分散式追蹤檢測 - .NET | Microsoft Learn
- System.Diagnostics 命名空間 | Microsoft Learn
- [OpenTelemetry] 現代化監控使用 OpenTelemetry 實現 : 在 .NET 如何使用 OpenTelemetry ~ m@rcus 學習筆記
埋點
一開始只要埋得廣就可以了,讓各個有相依到的服務都能串聯起來,之後有必要時再來針對性地埋點,而且埋得愈多資料量愈大,後續可能就會衍生出空間和查詢效能的問題。
提供名詞的對照,這樣比較好理解在 .NET 裡的物件對應到什麼:
OpenTelemetry | .NET |
---|---|
Tracer | ActivitySource |
Span | Activity |
注意事項:
- ActivitySource:建議只建立一次,儲存在靜態變數中,同一個元件中都使用同一個 ActivitySource。如果想要獨立控制的話,請再建立一個新的 ActivitySource。ActivitySource 是一個用來建立和啟動 Activity 的物件。
- Activity:在開始和停止的範圍內,操作要記錄的內容。
下面提供兩種記錄的方式,任君選擇。
方法一:手動為每個方法埋點
比較繁雜,但是操作空間大,能夠輕鬆微調自定義的標籤。
- 建立 ActivitySource
Instrumentation.cs
1 | namespace OtelSample.Service; |
- Activity 開始記錄
TestService.cs
1 | namespace OtelSample.Service; |
方法二:使用 AOP 套件
這種方式在類別或是方法上掛上自己寫好的 attribute 後,就能自動為目標方法在執行前或執行後做一些事,概念和 MVC 中的 Filter 一樣。AOP 的概念網路上的文章很多,搜尋一下就有。有興趣想看怎麼實作一個 AOP 框架和原理的話,可以看看蔣金楠的全新升级的AOP框架Dora.Interception[6]: 框架设计和实现原理。
.NET 有很多 AOP 套件可以選用,例如:
範例中將使用 Aspect Injector,詳細用法可以看 GitHub,或者看 [料理佳餚] C# 一個 Open Source 的 Compile-time AOP 框架 - AspectInjector - 軟體廚房,就不在這介紹用法。同時,也會將這個 attribute 設為共用元件,讓多個元件都能使用,所以會使用反射的方式來取得元件、類別、方法等名稱。
安裝 Aspect Injector 套件
1
AspectInjector 2.8.1
建立 ActivitySource
將這個類別作為取得各元件 ActivitySource 的集中處。先用元件名稱查詢有無這個名稱的 AcitvitySource,有就繼續沿用,沒有則新增一個並存下來。
Instrumentation.cs
1 | namespace OtelSample.Common; |
- 定義攔截器和 Advice
我們只需要記錄 public 的方法,並使用 Around 類型來包覆整個方法。
TracingAspect.cs
1 | namespace OtelSample.Common; |
- 建立 attribute
TracingAttribute.cs
1 | namespace OtelSample.Common; |
- 掛載 attribute
TestService.cs
1 | namespace OtelSample.Service; |
設定 OpenTelemetry
程式都埋好點之後(或不埋點),接下來就可以交由 OpenTelemetry 來處理了。
Program.cs
1 | // 反射取得服務相關的類別庫名稱 |
最後
呼叫幾次 API,再看看你的 APM 工具,享受精美的圖表。
實作時遇到什麼問題,或覺得範例寫得不完整,可以去看我 GitHub 的 Repository:zamhsu/Otel-WebApiSample
參考
- .NET | OpenTelemetry
- 新增分散式追蹤檢測 - .NET | Microsoft Learn
- System.Diagnostics 命名空間 | Microsoft Learn
- opentelemetry-dotnet - GitHub
- opentelemetry-dotnet-contrib - GitHub
- [OpenTelemetry] 現代化監控使用 OpenTelemetry 實現 : 在 .NET 如何使用 OpenTelemetry ~ m@rcus 學習筆記
- aspect-injector - GitHub