欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在.NET中使用DiagnosticSource的方法

 更新時(shí)間:2020年10月06日 10:06:31   作者:HueiFeng  
這篇文章主要介紹了在.NET中使用DiagnosticSource的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

DiagnosticSource是一個(gè)非常有意思的且非常有用的API,對(duì)于這些API它們?cè)试S不同的庫(kù)發(fā)送命名事件,并且它們也允許應(yīng)用程序訂閱這些事件并處理它們,它使我們的消費(fèi)者可以在運(yùn)行時(shí)動(dòng)態(tài)發(fā)現(xiàn)數(shù)據(jù)源并且訂閱與其相關(guān)的數(shù)據(jù)源。

DiagnosticSource在AspNetCore、EntityFrameworkCore、HttpClient、SqlClient中被使用,在我們實(shí)際的開(kāi)發(fā)過(guò)程中他使我們能夠進(jìn)行攔截請(qǐng)求與響應(yīng)的http請(qǐng)求、數(shù)據(jù)庫(kù)查詢(xún)、對(duì)HttpContext、DbConnection、DbCommand、HttpRequestMessageand等對(duì)象的訪問(wèn),甚至說(shuō)在需要的時(shí)候我們可以進(jìn)行修改這些對(duì)象來(lái)處理我們的業(yè)務(wù)。

下面我們將通過(guò)如下的簡(jiǎn)單示例來(lái)了解它.

DiagnosticSource和EventSource區(qū)別

DiagnosticSource和EventSource在架構(gòu)設(shè)計(jì)上很相似,他們的主要區(qū)別是EventSource它記錄的數(shù)據(jù)是可序列化的數(shù)據(jù),會(huì)被進(jìn)程外消費(fèi),所以要求記錄的對(duì)象必須是可以被序列化的。而DiagnosticSource被設(shè)計(jì)為在進(jìn)程內(nèi)處理數(shù)據(jù),所以我們通過(guò)它拿到的數(shù)據(jù)信息會(huì)比較豐富一些,它支持非序列化的對(duì)象,比如HttpContext、HttpResponseMessage等。另外如果想在EventSource中獲取DiagnosticSource中的事件數(shù)據(jù),可以通過(guò)DiagnosticSourceEventSource這個(gè)對(duì)象來(lái)進(jìn)行數(shù)據(jù)橋接。

需求來(lái)了

為了更好的理解DiagnosticSource的工作方式,如下這個(gè)示例將攔截?cái)?shù)據(jù)庫(kù)請(qǐng)求,假設(shè)我們有一個(gè)簡(jiǎn)單的控制臺(tái)應(yīng)用程序,它向數(shù)據(jù)庫(kù)發(fā)出請(qǐng)求并將結(jié)果輸出到控制臺(tái)。

class Program
{
  public const string ConnectionString =
    @"Server=localhost;Database=master;Trusted_Connection=True;";
  static async Task Main(string[] args)
  {
    var result = await Get();
    Console.WriteLine(result);

  }
  public static async Task<int> Get() {
    using (var connection=new SqlConnection(ConnectionString)) 
    {
      return await connection.QuerySingleAsync<int>("SELECT 42;");
    }
  }
}

我們?cè)賮?lái)思考一下,假設(shè)來(lái)了一個(gè)需求:我們需要獲取到所有數(shù)據(jù)庫(kù)查詢(xún)的執(zhí)行時(shí)間,或者說(shuō)我們要進(jìn)行獲取執(zhí)行的一些sql語(yǔ)句或者數(shù)據(jù)進(jìn)行存儲(chǔ)作為記錄我們?cè)撊绾翁幚恚?br /> 好了下面我們將嘗試使用DiagnosticSource來(lái)實(shí)現(xiàn)該需求。

使用System.Diagnostics.DiagnosticSource

來(lái)吧,我們先來(lái)創(chuàng)建一個(gè)類(lèi)作為該事件的處理程序或者說(shuō)作為該事件的消費(fèi)者。

public sealed class ExampleDiagnosticObserver
{}

下面我們將處理該事件,我們需要將這個(gè)類(lèi)進(jìn)行實(shí)例化,并且將它注冊(cè)到靜態(tài)對(duì)象中的觀察器中DiagnosticListener.AllListeners,代碼如下所示:

static async Task Main(string[] args)
{
  var observer = new ExampleDiagnosticObserver();
  IDisposable subscription = DiagnosticListener.AllListeners.Subscribe(observer);
  var result = await Get();
  Console.WriteLine(result);
}

下面我們?cè)賮?lái)修改我們的ExampleDiagnosticObserver類(lèi),其實(shí)如上代碼片段中編譯器已經(jīng)提醒我們要實(shí)現(xiàn)接口IObserver<diagnosticsListener>,下面我們實(shí)現(xiàn)它

public sealed class ExampleDiagnosticObserver : IObserver<DiagnosticListener>
{
  public void OnCompleted()
  { 
  }

  public void OnError(Exception error)
  { 
  }

  public void OnNext(DiagnosticListener value)
  {
    Console.WriteLine(value.Name);
  }
}

接下來(lái)我們運(yùn)行該程序,結(jié)果將在控制臺(tái)進(jìn)行打印如下所示:

SqlClientDiagnosticListener
SqlClientDiagnosticListener
42

看如上結(jié)果,這意味著在我們當(dāng)前這個(gè)應(yīng)用程序中的某個(gè)地方注冊(cè)了兩個(gè)類(lèi)型為DiagnosticListener的對(duì)象,名字為SqlClientDiagnosticListener。

對(duì)于應(yīng)用程序中創(chuàng)建的每個(gè)實(shí)例diagnosticsListener,在第一次使用時(shí)將調(diào)用IObserver<DiagnosticListener>.OnNext方法一次,現(xiàn)在我們只是將實(shí)例的名稱(chēng)輸出到了控制臺(tái)中,但實(shí)際情況中我們想一下,我們應(yīng)該對(duì)這個(gè)實(shí)例名稱(chēng)做什么?對(duì),沒(méi)錯(cuò),我們要對(duì)這些實(shí)例名稱(chēng)做檢查,那么我們?nèi)绻獙?duì)這個(gè)實(shí)例中某些事件,我們只需要使用subscribe方法去訂閱它。

下面我們來(lái)實(shí)現(xiàn)IObserver<DiagnosticListener>:

public class ExampleDiagnosticObserver1 : IObserver<DiagnosticListener>,
     IObserver<KeyValuePair<string, object>>
{
  private readonly List<IDisposable> _subscriptions = new List<IDisposable>();

  public void OnCompleted()
  {
  }

  public void OnError(Exception error)
  {
  }

  public void OnNext(KeyValuePair<string, object> value)
  {
    Write(value.Key, value.Value);
  }

  public void OnNext(DiagnosticListener value)
  {
    if (value.Name == "SqlClientDiagnosticListener")
    {
      var subscription = value.Subscribe(this);
      _subscriptions.Add(subscription);
    }
  }

  private void Write(string name, object value)
  {
    Console.WriteLine(name);
    Console.WriteLine(value);
    Console.WriteLine();
  }
}

在如上代碼片段中我們實(shí)現(xiàn)了接口IObserver<KeyValuePair<string, object>>的IObserver<KeyValuePair<string,object>>.OnNext的方法,參數(shù)為KeyValuePair<string,object>,其中Key是事件的名稱(chēng),而Value是一個(gè)匿名對(duì)象.

運(yùn)行程序輸出結(jié)果如下所示:

System.Data.SqlClient.WriteConnectionOpenBefore
{ OperationId = f5f4d4f0-7aa1-46e6-bd48-78acca3dac0a, Operation = OpenAsync, Connection = System.Data.SqlClient.SqlConnection, Timestamp = 1755845041766 }

System.Data.SqlClient.WriteCommandBefore
{ OperationId = 3d8617d1-0317-4f75-bffd-5b0fddf5cc12, Operation = ExecuteReaderAsync, ConnectionId = 554f4ee4-47c3-44ff-a967-cc343d1d5019, Command = System.Data.SqlClient.SqlCommand }

System.Data.SqlClient.WriteConnectionOpenAfter
{ OperationId = f5f4d4f0-7aa1-46e6-bd48-78acca3dac0a, Operation = OpenAsync, ConnectionId = 554f4ee4-47c3-44ff-a967-cc343d1d5019, Connection = System.Data.SqlClient.SqlConnection, Statistics = System.Data.SqlClient.SqlStatistics+StatisticsDictionary, Timestamp = 1755851869508 }

System.Data.SqlClient.WriteCommandAfter
{ OperationId = 3d8617d1-0317-4f75-bffd-5b0fddf5cc12, Operation = ExecuteReaderAsync, ConnectionId = 554f4ee4-47c3-44ff-a967-cc343d1d5019, Command = System.Data.SqlClient.SqlCommand, Statistics = System.Data.SqlClient.SqlStatistics+StatisticsDictionary, Timestamp = 1755853467664 }

System.Data.SqlClient.WriteConnectionCloseBefore
{ OperationId = ed240163-c43a-4394-aa2d-3fede4b27488, Operation = Close, ConnectionId = 554f4ee4-47c3-44ff-a967-cc343d1d5019, Connection = System.Data.SqlClient.SqlConnection, Statistics = System.Data.SqlClient.SqlStatistics+StatisticsDictionary, Timestamp = 1755854169373 }

System.Data.SqlClient.WriteConnectionCloseAfter
{ OperationId = ed240163-c43a-4394-aa2d-3fede4b27488, Operation = Close, ConnectionId = 554f4ee4-47c3-44ff-a967-cc343d1d5019, Connection = System.Data.SqlClient.SqlConnection, Statistics = System.Data.SqlClient.SqlStatistics+StatisticsDictionary, Timestamp = 1755854291040 }

42

如上結(jié)果可以清楚的看到里面存在6個(gè)事件,我們可以看到兩個(gè)是在打開(kāi)數(shù)據(jù)庫(kù)之前和之后執(zhí)行的,兩個(gè)是在執(zhí)行命令之前和之后執(zhí)行的,還有兩個(gè)是在關(guān)閉數(shù)據(jù)庫(kù)連接之前和之后執(zhí)行的。

另外可以看到每個(gè)事件中都包含一組參數(shù),如OperationId、Operation、ConnectionId等,這些參數(shù)通常作為匿名對(duì)象屬性傳輸,我們可以通過(guò)反射來(lái)獲取這些屬性的類(lèi)型化的值。

現(xiàn)在我們解決了我們最初的需求,獲取數(shù)據(jù)庫(kù)中所有查詢(xún)的執(zhí)行時(shí)間,并將其輸出到控制臺(tái)中,我們需要進(jìn)行修改,代碼如下所示:

private readonly AsyncLocal<Stopwatch> _stopwatch = new AsyncLocal<Stopwatch>();

private void Write(string name, object value)
{
  switch (name)
  {
    case "System.Data.SqlClient.WriteCommandBefore":
    {
      _stopwatch.Value = Stopwatch.StartNew();
      break;
    }
    case "System.Data.SqlClient.WriteCommandAfter":
    {
    var stopwatch = _stopwatch.Value;
    stopwatch.Stop();
    var command = GetProperty<SqlCommand>(value, "Command");
    Console.WriteLine($"CommandText: {command.CommandText}");
    Console.WriteLine($"Elapsed: {stopwatch.Elapsed}");
    Console.WriteLine();
    break;
    }
  }
}

private static T GetProperty<T>(object value, string name)
{
  return (T)value.GetType()
        .GetProperty(name)
        .GetValue(value);
}

在這我們將攔截?cái)?shù)據(jù)庫(kù)中查詢(xún)的開(kāi)始和結(jié)束事件,在執(zhí)行之前我們創(chuàng)建并且啟動(dòng)stopwatch,將其存儲(chǔ)在AsyncLocal<stopwatch>中,以后面將其返回,在執(zhí)行完成后,我們獲取之前啟動(dòng)的stopwatch,停止它,通過(guò)反射從參數(shù)值中獲取執(zhí)行命令,并將結(jié)果輸出到控制臺(tái)。

執(zhí)行結(jié)果如下所示:

CommandText: SELECT 42;
Elapsed: 00:00:00.1509086

42

現(xiàn)在我們已經(jīng)解決了我們的需求,但是目前還存在一個(gè)小的問(wèn)題,當(dāng)我們訂閱事件diagnosticListener時(shí),我們從它里面將接收到所有的事件,包括我們不需要的事件,但是呢發(fā)送的每個(gè)事件都會(huì)創(chuàng)建一個(gè)帶有參數(shù)的匿名對(duì)象,這會(huì)在GC上造成額外的壓力。

我們需要解決如上的問(wèn)題,避免我們?nèi)ヌ幚硭械氖录?,我們需要指定Predicate<string>這個(gè)特殊的委托類(lèi)型,我們聲明IsEnabled方法,在此篩選對(duì)應(yīng)名稱(chēng)的消費(fèi)者。

下面我們修改一下方法IObserver<DiagnosticListener>.OnNext

public void OnNext(DiagnosticListener value)
{
  if (value.Name == "SqlClientDiagnosticListener")
  {
    var subscription = value.Subscribe(this, IsEnabled);
    _subscriptions.Add(subscription);
  }
}
    
private bool IsEnabled(string name)
{
  return name == "System.Data.SqlClient.WriteCommandBefore"
    || name == "System.Data.SqlClient.WriteCommandAfter";
}

現(xiàn)在我們只會(huì)對(duì)事件System.Data.SqlClient.WriteCommandBefore和System.Data.SqlClient.WriteCommandAfter調(diào)用Write方法。

使用Microsoft.Extensions.DiagnosticAdapter

上面雖然我們實(shí)現(xiàn)了需求,但是我們也可以發(fā)現(xiàn)我們從DiagnosticListener接收到的事件參數(shù)通常作為匿名對(duì)象傳遞,因此通過(guò)反射去處理這些參數(shù)這樣給我們?cè)斐闪吮容^昂貴的消耗,不過(guò)開(kāi)發(fā)團(tuán)隊(duì)也考慮到了該問(wèn)題向我們提供了Microsoft.Extensions.DiagnosticAdapter來(lái)完成我們的操作。

下面我們需要將Subscribe改為SubscribeWithAdapter,另外在這種情況下我們不需要實(shí)現(xiàn)IObserver<KeyValuePair<string, object>>接口,相反的是我們需要為每個(gè)事件聲明一個(gè)單獨(dú)的方法,并且使用[DiagnosticNameAttribute]特性去標(biāo)注

如下所示:

public class ExampleDiagnosticObserver4 : IObserver<DiagnosticListener>
{
  private readonly List<IDisposable> _subscriptions = new List<IDisposable>();
  private readonly AsyncLocal<Stopwatch> _stopwatch = new AsyncLocal<Stopwatch>();

  public void OnCompleted()
  {
  }

  public void OnError(Exception error)
  {
  }

  public void OnNext(DiagnosticListener value)
  {
    if (value.Name == "SqlClientDiagnosticListener")
    {
      var subscription = value.SubscribeWithAdapter(this);
      _subscriptions.Add(subscription);
    }
  }

  [DiagnosticName("System.Data.SqlClient.WriteCommandBefore")]
  public void OnCommandBefore()
  {
    _stopwatch.Value = Stopwatch.StartNew();
  }

  [DiagnosticName("System.Data.SqlClient.WriteCommandAfter")]
  public void OnCommandAfter(DbCommand command)
  {
    var stopwatch = _stopwatch.Value;
    stopwatch.Stop();
    Console.WriteLine($"CommandText: {command.CommandText}");
    Console.WriteLine($"Elapsed: {stopwatch.Elapsed}");
    Console.WriteLine();
  }
}

現(xiàn)在我們實(shí)現(xiàn)了對(duì)數(shù)據(jù)執(zhí)行的監(jiān)控或者說(shuō)攔截功能,同時(shí)也能為我們的數(shù)據(jù)庫(kù)執(zhí)行時(shí)間做記錄,并且特別注意的是我們并沒(méi)有對(duì)應(yīng)用程序本身做修改,這樣也減輕了很多的冗余,同時(shí)節(jié)省了大量的編碼時(shí)間。這是一個(gè)很不錯(cuò)的編程體驗(yàn)。

創(chuàng)建DiagnosticListener實(shí)例

在大多數(shù)情況下,我們對(duì)DiagnosticSource都會(huì)去訂閱已經(jīng)存在的事件,基本我們都不需要去創(chuàng)建自己的DiagnosticListener去發(fā)送事件,當(dāng)然去了解一下這一特性也是比較好的,請(qǐng)繼續(xù)往下看

創(chuàng)建自己的實(shí)例

private static readonly DiagnosticSource diagnosticSource =
  new DiagnosticListener("MyLibraty");

發(fā)送事件,我們將調(diào)用Write進(jìn)行寫(xiě)入事件

if (diagnosticSource.IsEnabled("MyEvent"))
  diagnosticSource.Write("MyEvent", new { /* parameters */ });

參考

https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/DiagnosticSourceUsersGuide.md

https://sudonull.com/post/3671-Using-the-DiagnosticSource-in-NET-Core-Theory

https://github.com/dotnet/runtime/issues/20992

https://github.com/hueifeng/BlogSample/tree/master/src/DiagnosticDemo

到此這篇關(guān)于在.NET中使用DiagnosticSource的方法的文章就介紹到這了,更多相關(guān).NET使用DiagnosticSource內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • ASP.NET Core實(shí)現(xiàn)多文件上傳

    ASP.NET Core實(shí)現(xiàn)多文件上傳

    這篇文章介紹了ASP.NET Core實(shí)現(xiàn)多文件上傳的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-01-01
  • asp.net在iframe中彈出信息并執(zhí)行跳轉(zhuǎn)問(wèn)題探討

    asp.net在iframe中彈出信息并執(zhí)行跳轉(zhuǎn)問(wèn)題探討

    本代碼將實(shí)現(xiàn)在iframe中彈出信息并執(zhí)行跳轉(zhuǎn),感興趣的朋友可以參考下
    2013-04-04
  • ASP.NET筆記之 Repeater的使用

    ASP.NET筆記之 Repeater的使用

    本篇文章小編為大家介紹,ASP.NET筆記之 Repeater的使用。需要的朋友參考下
    2013-04-04
  • C#反射的一些應(yīng)用

    C#反射的一些應(yīng)用

    初始聽(tīng)說(shuō)反射是可以動(dòng)態(tài)的調(diào)用程序集,并從中來(lái)獲取相應(yīng)的方法和屬性,感覺(jué)比較神奇,,,
    2013-02-02
  • .Net 單例模式(Singleton)

    .Net 單例模式(Singleton)

    單例模式的意思就是只有一個(gè)實(shí)例。單例模式確保某一個(gè)類(lèi)只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。這個(gè)類(lèi)稱(chēng)為單例類(lèi)
    2013-07-07
  • asp.net利用存儲(chǔ)過(guò)程實(shí)現(xiàn)模糊查詢(xún)示例分享

    asp.net利用存儲(chǔ)過(guò)程實(shí)現(xiàn)模糊查詢(xún)示例分享

    這篇文章主要介紹了asp.net利用存儲(chǔ)過(guò)程實(shí)現(xiàn)模糊查詢(xún)的示例,大家參考使用吧
    2014-01-01
  • asp.net發(fā)郵件的幾種方法匯總

    asp.net發(fā)郵件的幾種方法匯總

    .net中發(fā)送郵件方法有很多,如MailMessage,SmtpMail等下面我來(lái)給大家利用這些方法來(lái)實(shí)現(xiàn)在.net中郵件發(fā)送吧,希望此方法對(duì)各位同學(xué)會(huì)有所幫助
    2014-01-01
  • .NET CORE中比較兩個(gè)文件內(nèi)容是否相同的最快方法

    .NET CORE中比較兩個(gè)文件內(nèi)容是否相同的最快方法

    這篇文章主要給大家介紹了關(guān)于.NET CORE中比較兩個(gè)文件內(nèi)容是否相同的最快方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用.NET CORE具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • asp.net下遍歷頁(yè)面中所有的指定控件的代碼

    asp.net下遍歷頁(yè)面中所有的指定控件的代碼

    遍歷aspx頁(yè)面中所有的指定控件的代碼
    2010-02-02
  • asp.net TripleDES加密、解密算法

    asp.net TripleDES加密、解密算法

    加密,使用密碼產(chǎn)生加密算法的公鑰,并使用TripleDES對(duì)密碼進(jìn)行加密。解密,使用密碼產(chǎn)生加密算法的公鑰,并使用TripleDES對(duì)加密數(shù)據(jù)進(jìn)行解密。
    2008-08-08

最新評(píng)論