1. 程式人生 > 程式設計 >在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤資訊

在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤資訊

前言

最新一直在忙著專案上的事情,很久沒有寫部落格了,在這裡對關注我的粉絲們說聲抱歉,後面我可能更多的分享我們在微服務落地的過程中的一些經驗。那麼今天給大家講一下在 .NET Core 2 中引入的全新 DiagnosticSource 事件機制,為什麼說是全新呢? 在以前的 .NET Framework 有心的同學應該知道也有 Diagnostics,那麼新的 .NET Core 中有什麼變化呢? 讓我們一起來看看吧。

Diagnostics

Diagnostics 一直是一個被大多數開發者忽視的東西,我猜測很多同學看到這裡的時候可能還是第一次聽說 Diagnostics 這個東西,為什麼會被忽視呢? 我們等會說,我們先來看一下 Diagnostics 是用來做什麼的。

Diagnostics 是什麼呢?

讓我們把時間往前拉回到 2013 年 8 月,微軟在 NuGet 釋出了一個新的關於 Diagnostics 的包叫做 Microsoft.Diagnostics.Tracing.TraceEvent,這個包用來為 Windows 事件追蹤(ETW)提供一個強大的支援,使用這個包可以很容易的為我們在雲環境和生產環境來提供端到端的監控日誌事件記錄,它輕量級,高效,並且可以和系統日誌進行互動。

PS:通過這個包我們可以獲取到 CLR 執行的一些細節資訊,由於本篇主題,對此不介紹過多了。

看到這個包提供的功能,那麼博主就自己總結一下,對 Diagnostics 下個定義 :在應用程式出現問題的時候,特別是出現可用性或者效能問題的時候,開發人員或者IT人員經常會對這些問題花費大量的時間來進行診斷,很多時候生產環境的問題都無法復現,這可能會對業務造成很大的影響,Diagnostics 就是提供一組功能使我們能夠很方便的可以記錄在應用程式執行期間發生的關鍵性操作以及他們的執行時間等,使管理員可以查詢特別是生產環境中出現問題所在的根本原因。

有同學可能會說了,這不就是 APM(Application Performance Management) 麼,嗯,從巨集觀的角度來說這屬於APM的一部分,但 APM 不僅僅只有這些。

.NET Framework 之 EventSource

在上面我們瞭解到了 Microsoft.Diagnostics.Tracing.TraceEvent,那麼相關搭配使用的還有兩個 NuGet 包就是 Microsoft.Diagnostics.Tracing.EventSou程式設計客棧rce 這個包,那我就簡單講一下,我不準備在這個部分講述太多,畢竟已經被替換掉了,我們來看下 EventSource。

EventSource

在 .NET Framework 中 EventSource 通過 Windows ETW 提供的 ETW Channels 與其進行整合,下面給出一個示例程式碼:

[EventSource(Name = "Samples-EventSourceDemos-Minimal")]
public sealed class MinimalEventSource : EventSource
{
    // Define singleton instance
    public static MinimalEventSource Log = new MinimalEventSource();

    // Define Event methods
    public void Load(long baseAddress,string imageName)
    {
        WriteEvent(1,baseAddress,imageName);
    }
}

那麼在 ETW 中我們就可以看到相關的事件資訊了:

在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤資訊

注意,在 .NET Framework 4.5 以及更高版本,EventSource 已經被整合到了 System 名稱空間。

學習,也是一個總結的過程,對此,我們也許可以總結出來一個比較重要的資訊就是:通過 Diagnostics 的名稱空間變化,由 Microsoft 變為了 System, 我們可以看到 Diagnostics 對於我們的應用程式來說變得更加重要了。

由於 EventSource 只支援 Windows,所以在全新的 .NET Core 中,它已經被悄悄的取代了,下面我們來看一下全新的 DiagnosticSource。

.NET Core 之 全新 DiagnosticSource

在 .NET Core 中 .NET 團隊設計了一個全新的 DiagnosticSource,新的 DiagnosticSource 非常的簡單,它允許你在生產環境記錄豐富的 payload 資料,然後你可以在另外一個消費者可以消費感興趣的記錄,是不是聽著有點懵逼?沒關係,等會我再詳細說。

我們先來說說 DiagnosticSource 和上面的 EventSource 的區別,他們的架構設計有點類似,主要區別是 EventSource 它記錄的資料是可序列化的資料,會被在程序外消費,所以要求記錄的物件必須是可以被序列化的。而 DiagnosticSource 被設計為在程序內處理資料,所以通過它可以拿到更加豐富的一些資料資訊,它支援非序列化的物件,比如 HttpContext,HttpResponseMessage 等。如果你想在 EventSource 中獲取 DiagnosticSource 中的事件資料,你可以通過 DiagnosticSourceEventSource 這個物件來進行資料橋接。

下面我們來看一下在程式碼中如何使用 DiagnosticSource物件。

在這之前我們需要了解另外一個物件 DiagnosticListen程式設計客棧er,DiagnosticListener 從命名上來看它是一個監聽診斷資訊的物件,它確實是一個用來接收事件的類,在 .NET Core 中 DiagnosticSource 它其實是一個抽象類,定義了記錄事件日誌所需要的方法,那麼我們在使用的時候就需要使用具體的物件,DiagnosticListener 就是 DiagnosticSource 的預設實現,明白了吧。

好了,現在我們來看一下如何使用吧。

生成 Diagnostic 日誌記錄

如何生成 Diagnostic 日誌記錄呢?首先,我們需要建立一個 DiagnosticListener 物件,比如:

private static DiagnosticSource httpLogger = new  DiagnosticListener("System.Net.Http");

DiagnosticListener 引數中的名稱即為需要監聽的事件(元件)名稱,這個名稱在以後會被用來被它的消費者所訂閱使用。

DiagnosticSource 其核心只包含了兩個方法,分別是 :

bool IsEnabled(string name)
void Write(string name,object value);

那麼然後我們可以這樣來呼叫:

if (httpLogger.IsEnabled("RequestStart")){
    httpLogger.Write("RequestStart",new { Url="http://clr",Request=aRequest });
}

IsEnabled(string param1) 這個方法用來判斷是否有消費者註冊了當前的事件(元件)名稱監聽,通常有消費者關心了相關資料,我們才會進行事件記錄。
Write(string param1,object param2) 這個方法用來向 DiagnosticSource 中寫入日誌記錄,param1 和上面一樣用來指定名稱的,也就是所向指定名稱中寫入資料,param2 即為寫入的 payloads 資料,你可以使用 匿名型別來向 param2 中寫入資料,這樣會方便很多。

這樣,我們就已經把 Diagnostic 事件日誌寫入到 DiagnosticSource中了,是不是很簡單? 我們再看一下如何進行消費(監聽)這些事件資訊。

監聽 Diagnostic 日誌記錄

在監聽 Diagnostic 日誌記錄之前你需要知道你要關心的事件資料名稱,那麼如果僅僅是在程式碼中把 DiagnosticListeners 都寫死到監聽的消費者程式碼中的話,這樣就太不靈活了,所以這裡設計了一個機制用來發現中那些在執行時被啟用的DiagnosticListeners

你可以使用 DiagnosticListener.AllListeners 來獲取一個 IObservable<DiagnosticListener>物件,IObservable介面大家應該都不陌生了吧(不太清楚的可以看這裡),然後通過其Subscribe方法進行OnNext“回撥”關心的事件資料。

示例程式碼:

static IDisposable networkSubscription = null;

// 使用 AllListeners 來獲取所有的DiagnosticListeners物件,傳入一個IObserver<DiagnosticListener> 回撥
static IDisposable listenerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener)
{
    // 當 DiagnosticsListener 啟用的時候,這裡將獲得一個回撥用
    if (listener.Name == "System.Net.Http")
    {
        // 訂閱者監聽消費程式碼
        lock(allListeners)
        {
            if (networkSubscription != null)
                networkSubscription.Dispose();
            
            //回撥業務程式碼
            Action<KeyValuePair<string,object>> callback = (KeyValuePair<string,object> evnt) =>
                Console.WriteLine("From Listener {0} Received Event {1} with payload {2}",networkListener.Name,evnt.Key,evnt.Value);
           
            //建立一個匿名Observer物件
            Observer<KeyValuePair<string,object>> observer = new AnonymousObserver<KeyValuePair<string,object>>(callback);
            
            //篩選你感興趣的事件
            Predicate<string> predicate = (string eventName) => eventName == "RequestStart";
            
            networkSubscription = listener.Subscribe(observer,predicate);
        }
    }
});

// 通常情況下,這裡你需要保持 listenerSubscription 始終處於啟用狀態,
// 如果你像取消回撥,你可以呼叫 listenerSubscription.Dispose() 來取消訂閱者

通過這種方式,我們就可以在觸發回撥的之後做一些我們想要的操作了。

是不是發現上面的那種寫法有點麻煩和醜陋,ASP.NET 團隊考慮到了,所以為我們封裝了一個介面卡的庫來方便我們進行監聽的一些操作,你可以通過打 attribute 標記的方式來進行相關事件的訂閱,有興趣的同學可以看下這個(Microsoft.Extensions.DiagnosticAdapte) NuGet 包。

現在我們已經可以拿到資料了,有同學可能會說在生產環境資料這麼多,這些資料我存到哪裡,又怎麼樣來處理呢,我不可能一條一條的來找效能在哪裡吧,OK,我們接著往下看。

為你的框架支援 Diagnostics

隨著微服務的流行,服務的鏈路追蹤以及應用程式的效能問題變得越來越重要,而 APM 也成為了整個微服務架構中很重要的一箇中間件,它可以協助我們快速查詢生產環境中所遇到的問題,以及在應用程式發生異常的時候收集異常執行時的上下文資訊來快速排查問題。

對 Google 的 Dapper 或者 OpenTracing 協議有了解的同學應該已經想到了,我們可以利用上面的那些資料按照這些協議的約定進行包裝,然後傳送到支援這些協議的 APM 的服務端,剩下的工作是不是可以由這些服務端來幫助我們處理了,包括圖形化展示,效能檢視,呼叫鏈檢視等。

大多數的開源APM專案都支援 Dapper 或者 OpenTracing 協議,如 Apache SkyWalking,ZipKin,pinpoint 等。 順便說一句,我們 NCC開源專案組 的 Lemon 同學正在給 SkyWalking 寫 C# 的 客戶端驅動專案 ,這是一項非常具有挑戰性的工作,感興趣的同學可以 Star 一下。

相信閱讀本篇文章也有不少的架構師,開源專案作者,框架開發者,甚至應用程式開發者,那麼我建議可以從現在開始對你的專案提供 Diagnostics 支援,目前 .NET Core 中 CoreFx,ASP.NET Core,EntityFramework Core 都已經對 Diagnostics 提供了支援。

CAP 在 2.2 版本中已經對 程式設計客棧Diagnostics 提供了支援。

CAP 中的 Diagnostics

CAP: https://github.com/dotnetcore/CAP

CAP 是我的一個開源專案,用來處理在微服務或者SOA架構中分散式事務的一個解決方案,你可以在這篇文章中看到更多關於 CAP 的介紹,喜歡的同學可以給個 Star ,也是我繼續做的更好的動力,謝謝。

CAP 對外提供的事件監聽者名稱為: CapDiagnosticListener

CAP 中的 Diagnostics 提供對外提供的事件資訊有:

  • 訊息持久化之前
  • 訊息持久化之後
  • 訊息持久化異常
  • 訊息向MQ傳送之前
  • 訊息向MQ傳送之後
  • 訊息向MQ傳送異常
  • 訊息從MQ消費儲存之前
  • 訊息從MQ消費儲存之後
  • 訂閱者方法執行之前
  • 訂閱者方法執行之後
  • 訂閱者方法執行異常

相關涉及到的物件,你可以在 DotNetCore.CAP.Diagnostics 名稱空間下看到。

基於這些對外的事件資料,我們可以來對接APM,下面這個是我對接的 ZipKin 的一個圖:

在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤資訊

總結

通過本篇文章我們知道了 .NET Core 中為我們提供的一個新的事件資料記錄物件DiagnosticSource ,通過gxwIsOM這個物件,我們可以對外提供一些診斷資訊,以便於在生產環境中對我們的應用程式進行效能問題排查和調程式設計客棧用鏈跟蹤,然後我們知道了一下CAP對外提供的一些Diagnostics事件。

以上就是在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤資訊的詳細內容,更多關於.NET Core 記錄跟蹤資訊的資料請關注我們其它相關文章!