在.NET中使用DiagnosticSource的方法

DiagnosticSource是一个非常有意思的且非常有用的API,对于这些API它们允许不同的库发送命名事件,并且它们也允许应用程序订阅这些事件并处理它们,它使我们的消费者可以在运行时动态发现数据源并且订阅与其相关的数据源。

DiagnosticSource在AspNetCore、EntityFrameworkCore、HttpClient、SqlClient中被使用,在我们实际的开发过程中他使我们能够进行拦截请求与响应的http请求、数据库查询、对HttpContext、DbConnection、DbCommand、HttpRequestMessageand等对象的访问,甚至说在需要的时候我们可以进行修改这些对象来处理我们的业务。

下面我们将通过如下的简单示例来了解它.

DiagnosticSource和EventSource区别

DiagnosticSource和EventSource在架构设计上很相似,他们的主要区别是EventSource它记录的数据是可序列化的数据,会被进程外消费,所以要求记录的对象必须是可以被序列化的。而DiagnosticSource被设计为在进程内处理数据,所以我们通过它拿到的数据信息会比较丰富一些,它支持非序列化的对象,比如HttpContext、HttpResponseMessage等。另外如果想在EventSource中获取DiagnosticSource中的事件数据,可以通过DiagnosticSourceEventSource这个对象来进行数据桥接。

需求来了

为了更好的理解DiagnosticSource的工作方式,如下这个示例将拦截数据库请求,假设我们有一个简单的控制台应用程序,它向数据库发出请求并将结果输出到控制台。

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;"); } } }

我们再来思考一下,假设来了一个需求:我们需要获取到所有数据库查询的执行时间,或者说我们要进行获取执行的一些sql语句或者数据进行存储作为记录我们该如何处理?
好了下面我们将尝试使用DiagnosticSource来实现该需求。

使用System.Diagnostics.DiagnosticSource

来吧,我们先来创建一个类作为该事件的处理程序或者说作为该事件的消费者。

public sealed class ExampleDiagnosticObserver {}

下面我们将处理该事件,我们需要将这个类进行实例化,并且将它注册到静态对象中的观察器中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); }

下面我们再来修改我们的ExampleDiagnosticObserver类,其实如上代码片段中编译器已经提醒我们要实现接口IObserver<diagnosticsListener>,下面我们实现它

public sealed class ExampleDiagnosticObserver : IObserver<DiagnosticListener> { public void OnCompleted() { } public void OnError(Exception error) { } public void OnNext(DiagnosticListener value) { Console.WriteLine(value.Name); } }

接下来我们运行该程序,结果将在控制台进行打印如下所示:

SqlClientDiagnosticListener
SqlClientDiagnosticListener
42

看如上结果,这意味着在我们当前这个应用程序中的某个地方注册了两个类型为DiagnosticListener的对象,名字为SqlClientDiagnosticListener。

对于应用程序中创建的每个实例diagnosticsListener,在第一次使用时将调用IObserver<DiagnosticListener>.OnNext方法一次,现在我们只是将实例的名称输出到了控制台中,但实际情况中我们想一下,我们应该对这个实例名称做什么?对,没错,我们要对这些实例名称做检查,那么我们如果要对这个实例中某些事件,我们只需要使用subscribe方法去订阅它。

下面我们来实现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(); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/3b4f2089b242a64a1cc5961f78c7634f.html