深入研究 .NET 5 的開放式遙測


OpenTelemetry 介紹

OpenTelemetry是一種開放的源代碼規范,工具和SDK,用於檢測,生成,收集和導出遙測數據(指標,日志和跟蹤),開放遙測技術得到了Cloud Native Computing Foundation(CNCF)的支持,該基金會支持一系列流行的優秀的開源項目,你可以去看一下CNCF景觀圖,https://landscape.cncf.io/ ,就明白了我的意思,這個SDK支持所有主要的編程語言,包括C#和ASP.NET Core。

在這篇文章中,我將討論OpenTelemetry的全部含義,為什么要使用它以及如何在.NET中使用,對於典型的應用程序,通常需要記錄三組數據:指標,日志和跟蹤。

Logging 日志

可以監聽程序的進程發出的消息日志,在.NET應用程序中,如果您使用NuGet包ILogger中的日志記錄功能,就可以輕松的讓OpenTelemetry支持 Microsoft.Extensions.Logging, 如果要構建ASP.NET Core應用程序,通常已經使用了此功能。

Metrics 指標

提供運行進程的指標信息,包括計數器,儀表盤和直方圖,對OpenTelemetry中指標的支持仍在開發中, 但是已經確定下來了,指標包括以下:

  • CPU 使用百分比
  • 進程內存使用量
  • Http的請求數量

Tracing 追蹤

也叫做分布式跟蹤,它記錄單個操作的開始和結束時間以及與該操作相關的參數,比如在ASP.NET Core中記錄HTTP請求的跟蹤,您可能會記錄請求和響應的開始和結束時間,參數將是 Http的請求方式,請求參數,請求地址等,請求調用會形成鏈路,您可以深入了解時間耗費在哪個服務,或者服務中有異常報錯發生。

Jaeger

收集指標,日志,追蹤信息只是一部分,如何進行數據處理,展示是APM系統的功能,因為收集的數據遵循OpenTelemetry標准,所以可以和APM系統完美結合。

Jaeger和Zipkin是可以收集和顯示並且與Open Telemetry兼容APM, Zipkin的話比較久了,並且沒有很好的UI,因此我個人推薦Jaeger,看起來像這樣:

上圖顯示了應用程序的跟蹤,您可以看到它如何使用HTTP請求對MySQL,Redis和外部API進行調用, 每行的長度顯示了執行所需的時間,您可以輕松地從頭到尾查看跟蹤中執行的所有主要操作,您還可以深入研究每一行,並查看與該部分跟蹤有關的其他信息。

Spans 跨度

上面Jaeger圖中的每一行都稱為 Span,在.NET中的每一行均由System.Activities.Activity類型表示,它也具有唯一的標識符,開始和結束時間以及父范圍的唯一標識符,所以這些可以形成調用鏈,並且Span還可以包含其他參數。

不幸的是,.NET團隊的命名大大偏離了官方的OpenTelemetry規范,我有點疑惑,不過我現在已經明白了大概。

我的理解是.NET已經包含一個Activity的類型,因此.NET團隊決定重用它,而不是重新創建一個 Span的新類型,這意味着很多命名與open-telemetry規范不匹配,在.NET中,你現在可以把 Span 和 Activity身份互換。

注意:在.NET 5中才有ActivitySource,在之前可以用 Activity。

使用Span記錄行為非常簡單,首先,我們必須創建一個ActivitySource可以記錄Span或活動的對象:

private static ActivitySource activitySource = new ActivitySource(
    "companyname.product.library",
    "semver1.0.0");

然后,我們可以調用StartActivity開始記錄,最后調用Dispose停止記錄Span:

using (var activity = activitySource.StartActivity("ActivityName")
{
    // Pretend to do some work.
    await LongRunningAsync().ConfigureAwait(false);
} // Activity gets stopped automatically at end of this block during dispose.

Events 事件

當Span開啟記錄后,我們也可以在這期間記錄事件,這些事件包含了時間戳信息:

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // Log timestamped events that can take place during an activity. 
    Activity.Current?.AddEvent(new ActivityEvent("Something happened."));
}

在LongRunningOperationAsync方法中,有一個問題是,如何把activity傳入這個方法,如果我們定義了一個參數,這體驗也太差了,但是,將兩個操作分離的一個好的方法是使用Activity.Current。

一個常見的錯誤,我可以預見的是,Activity.Current可能是null,所以這里我加了null判斷。

Attributes 屬性

屬性是數據的鍵值對,您可以將其記錄為單個Span的一部分,比如Http的請求方式,請求狀態碼等。

注意,在Open Telemetry規范中叫 Attributes,在 我們.NET 中叫Tag

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // Log an attribute containing arbitrary data.
    Activity.Current?.SetTag("http.method", "GET");
}

IsRecording 記錄

IsRecording是Span上的一個標志,如果返回false,表明該Span已結束,另外,如果你的數據量比較大的話,你需要抽樣采集,比如10%,那么你也可以手動把這些span設置為false,它不會采集。

注意:在open-telemetryg規范中叫IsRecording,在.NET Core 3.1中是 Recorded,在.NET 5 中是 IsAllDataRequested。

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // It's possible to optionally request more data from a particular span.
    var activity = Activity.Current;
    if (activity != null && activity.IsAllDataRequested)
    {
        activity.SetTag("http.url", "http://www.mywebsite.com");
    }
}

Trace的語義約定

注意屬性名稱http.method,http.url,我在以上示例中使用了該屬性,因為在open-telemetry規范中已經標准化了某些常用的屬性名稱,標准化常用屬性名稱可以在Jaeger等APM中很好的展示它們,屬性名稱已分類為幾個不同的類別,你可以花點時間看一下:

  • General: 可用於描述不同種類的操作的常規語義屬性
  • HTTP: 客戶端和服務器的Http調用
  • Database: SQL 和 NoSql的調用
  • RPC/RMI: 遠程調用,比如gRPC等
  • Messaging: 用於與消息傳遞系統(隊列,發布/訂閱等)
  • Exceptions: 用於記錄與Span關聯的異常的屬性

Exporting 導出

有很多用於導出使用OpenTelemetry收集的數據的插件,我將在我的下一篇博客文章中討論有關在ASP.NET Core中使用Open Telemetry的信息, 可以很方便的處理這些數據,您可以輕松地訂閱然后消費OpenTelemetry數據,如下所示:

using var subscriber = DiagnosticListener.AllListeners.Subscribe(
    listener =>
    {
        Console.WriteLine($"Listener name {listener.Name}");

        listener.Subscribe(kvp => Console.WriteLine($"Received event {kvp.Key}:{kvp.Value}"));
    });

跨進程的追蹤

為什么這些程序會形成調用鏈,它們是不同的進程,這個怎么實現的呢?

這就是W3C跟蹤上下文標准,它定義了一系列HTTP Header,這些Header將有關當前正在記錄的任何跟蹤的信息從一個進程傳遞到另一個進程,它通過Http的Header來傳遞信息,規范中定義了兩個HTTP Header:

  • traceparent-包含version,trace-id,parent-id和trace-flags

    • version - 在open-telemetry規范中,它始終是00
    • trace-id - 跟蹤的唯一標識符。
    • parent-id -作為當前 patent span 的唯一標識符。
    • trace-flags -當前跟蹤的一組標志,用於確定是否正在采樣當前跟蹤以及跟蹤級別。
  • tracestate -由一組名稱/值對表示的特定於供應商的數據。

traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: asp=00f067aa0ba902b7,redis=t61rcWkgMzE

Baggage

與Attributes類似,Baggage是我們可以將數據作為鍵值對添加到跟蹤的另一種方式,不同之處在於,Baggage使用W3C規范中baggage定義的HTTP Header跨進程邊界傳遞,但是Attributes的值數據只在當前Span中可用

baggage: userId=alice,serverNode=DF:28,isProduction=false

使用的話,也有些相同之處:

using (var activity = activitySource.StartActivity("ActivityName")
{
    await LongRunningOperation().ConfigureAwait(false);
}

public async Task LongRunningOperationAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);

    // Log an attribute containing arbitrary data.
    Activity.Current?.AddBaggage("http.method", "GET");
}

它的用途在於,比如說我需要傳遞一個訂單ID,我就可以放到 Baggage 數據中,它在整個請求鏈路中都可以訪問。

總結

.NET團隊對OpenTelemetry非常重視,你可以看到Activity類型在.NET 5 中的增強,並且默認 HttpClient 調用時,它會自動傳輸W3C跟蹤上下文HTTP Header, 基於ILogger的統一日志,也可以很好的收集和OpenTelemetry兼容的日志。

原文作者: Rehan Saeed
原文鏈接: https://rehansaeed.com/deep-dive-into-open-telemetry-for-net/

最后

歡迎掃碼關注我們的公眾號 【全球技術精選】,專注國外優秀博客的翻譯和開源項目分享,也可以添加QQ群 897216102


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM