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

OpenTelemetry 介紹

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

在這篇文章中,我將討論OpenTelemetry的所有含義,爲何要使用它以及如何在.NET中使用,對於典型的應用程序,一般須要記錄三組數據:指標,日誌和跟蹤。redis

Logging 日誌

能夠監聽程序的進程發出的消息日誌,在.NET應用程序中,若是您使用NuGet包ILogger中的日誌記錄功能,就能夠輕鬆的讓OpenTelemetry支持 Microsoft.Extensions.Logging, 若是要構建ASP.NET Core應用程序,一般已經使用了此功能。編程

Metrics 指標

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

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

Tracing 追蹤

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

Jaeger

收集指標,日誌,追蹤信息只是一部分,如何進行數據處理,展現是APM系統的功能,由於收集的數據遵循OpenTelemetry標準,因此能夠和APM系統完美結合。async

Jaeger和Zipkin是能夠收集和顯示而且與Open Telemetry兼容APM, Zipkin的話比較久了,而且沒有很好的UI,所以我我的推薦Jaeger,看起來像這樣:編程語言

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

Spans 跨度

上面Jaeger圖中的每一行都稱爲 Span,在.NET中的每一行均由System.Activities.Activity類型表示,它也具備惟一的標識符,開始和結束時間以及父範圍的惟一標識符,因此這些能夠造成調用鏈,而且Span還能夠包含其餘參數。工具

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

個人理解是.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

相關文章
相關標籤/搜索