经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
使用System.Diagnostic.DiagnosticListener 实现对应用程序的监听
来源:cnblogs  作者:aimigi  时间:2021/3/1 15:29:40  对本文有异议

1. System.Diagnostics概述

System.Diagnostics 是一个偏底层的命名空间,有一系列API,由Runtime 层提供,应用层可以通过它与系统集成、事件日志、以及性能计数器进行交互。
在.NetCore 源码包中,提供了如下几个dll.

大致描述如下:

由于本文只讨论诊断日志,所以仅重点讨论System.Diagnostics.DiagnosticSource

2. DiagnosticSource 概述

  • DiagnosticSource 是一个抽象类,表示诊断日志来源,使用时需要用DiagnosticListener实例化。
  • DiagnosticSource 和EventSource在设计上很相似,主要区别在于EventSource记录的是可序列化的数据,会被外部进程消费。而DiagnosticSource 被设计为进程内处理数据,所以通过它拿到的数据会比较丰富,支持非序列化对象,如HttpContext、HttpResponseMessage等。

2.1 DiagnosticListener 与观察者

先看一下DiagnosticSource和DiagnosticListener 类定义(.NetCore3.1):

  1. // DiagnosticSource 诊断源类定义
  2. public abstract class DiagnosticSource
  3. {
  4. protected DiagnosticSource();
  5. public abstract bool IsEnabled(string name);
  6. public virtual void OnActivityExport(Activity activity, object payload);
  7. public virtual void OnActivityImport(Activity activity, object payload);
  8. public Activity StartActivity(Activity activity, object args);
  9. public void StopActivity(Activity activity, object args);
  10. public abstract void Write(string name, object value);
  11. }
  12. // DiagnosticListener 类定义
  13. public class DiagnosticListener : DiagnosticSource, IDisposable, IObservable<KeyValuePair<string, object>>
  14. {
  15. public DiagnosticListener(string name);
  16. public static IObservable<DiagnosticListener> AllListeners { get; }
  17. public string Name { get; }
  18. public virtual void Dispose();
  19. public bool IsEnabled();
  20. public override bool IsEnabled(string name);
  21. public override bool IsEnabled(string name, object arg1, object arg2 = null);
  22. public override void OnActivityExport(Activity activity, object payload);
  23. public override void OnActivityImport(Activity activity, object payload);
  24. public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer);
  25. public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer, Func<string, object, object, bool> isEnabled);
  26. public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer, Predicate<string> isEnabled);
  27. public virtual IDisposable Subscribe(IObserver<KeyValuePair<string, object>> observer, Func<string, object, object, bool> isEnabled, Action<Activity, object> onActivityImport = null, Action<Activity, object> onActivityExport = null);
  28. public override void Write(string name, object value);
  29. }

以上定义可以发现DiagnosticListener 是DiagnosticSource的具体实现,并且它是一个可观察主题。
有关IObserver-IObservable 可以从这篇博客了解: C#实现观察者模式
自己创建可观察主题时一般按如下操作:
1)创建发布者,2)创建观察者并将其注册到发布者上。

  1. DiagnosticSource httpLogger = new DiagnosticListener("PubSubject");
  2. // 检查监听的组件是否有RequestStart 这个事件
  3. if (httpLogger.IsEnabled("PubSubject.Name"))
  4. {
  5. /*
  6. * Write 方法接受两个参数
  7. * param1 表示事件名
  8. * param2 是要写入的数据,这个数据会被可观察对象在发生这个事件时抛出抛出
  9. */
  10. httpLogger.Write("PubSubject.Name", new { Url = "http://test.com", Request = new { Name = "ddd",Value="ttttt" } });
  11. }
  12. // 为发布者注册订阅者(观察者)
  13. DiagnosticListener.AllListeners.Subscribe(new MyObserver<DiagnosticListener>(listener=> {
  14. if(listener.Name== "PubSubject")
  15. {
  16. listener.Subscribe(new MyObserver<KeyValuePair<string, object>>(listenerData =>
  17. {
  18. Console.WriteLine($"Listening Name:{listenerData.Key}");
  19. dynamic data = listenerData.Value;
  20. Console.WriteLine($"Listening Data Name:{data.Name} Value:{data.Value}");
  21. }));
  22. }
  23. }));

大多数情况下我们使用DiagnosticListener时,不需要发布事件,而仅仅作为观察者跟踪事件进行处理,毕竟常用基础类库中已经埋下了足够多的事件诊断点。

3. SqlClient库中的埋点事件(System.Data.SqlClient v4.8.2)

SqlClient 是Ado.Net 中操作数据库的基础对象,使用DiagnosticListener 可以对这个对象的操作进行一些Hack操作。

  1. // 定义一个观察者
  2. public class MyObserver<T> :IObserver<T>
  3. {
  4. private Action<T> _next;
  5. public MyObserver(Action<T> next)
  6. {
  7. _next = next;
  8. }
  9. public void OnCompleted()
  10. {
  11. }
  12. public void OnError(Exception error)
  13. {
  14. }
  15. public void OnNext(T value)
  16. {
  17. _next(value);
  18. }
  19. }
  20. // 一个参数是KeyValuePair<string, object> 的观察者
  21. public class ExampleDiagnosticObserver : IObserver<DiagnosticListener>, IObserver<KeyValuePair<string, object>>
  22. {
  23. private readonly List<IDisposable> disposables = new List<IDisposable>();
  24. public void OnCompleted()
  25. {
  26. }
  27. public void OnError(Exception error)
  28. {
  29. }
  30. public void OnNext(KeyValuePair<string, object> value)
  31. {
  32. Write(value.Key, value.Value);
  33. }
  34. public void OnNext(DiagnosticListener value)
  35. {
  36. var subscription = value.Subscribe(this);
  37. disposables.Add(subscription);
  38. }
  39. private void Write(string name, object value)
  40. {
  41. Console.WriteLine(name);
  42. Console.WriteLine(value);
  43. Console.WriteLine();
  44. }
  45. }
  46. /// <summary>
  47. /// 使用Microsoft.Extensions.DiagnosticAdapter
  48. /// </summary>
  49. public class SqlClientObserver : IObserver<DiagnosticListener>
  50. {
  51. private readonly List<IDisposable> _subscriptions = new List<IDisposable>();
  52. private readonly AsyncLocal<Stopwatch> _stopwatch = new AsyncLocal<Stopwatch>();
  53. public void OnCompleted()
  54. {
  55. }
  56. public void OnError(Exception error)
  57. {
  58. }
  59. public void OnNext(DiagnosticListener value)
  60. {
  61. if(value.Name== "SqlClientDiagnosticListener")
  62. {
  63. var subscription= value.SubscribeWithAdapter(this);
  64. _subscriptions.Add(subscription);
  65. }
  66. }
  67. [DiagnosticName("System.Data.SqlClient.WriteCommandBefore")]
  68. public void OnCommandBefore()
  69. {
  70. _stopwatch.Value = Stopwatch.StartNew();
  71. }
  72. [DiagnosticName("System.Data.SqlClient.WriteCommandAfter")]
  73. public void OnCommandAfter(DbCommand command)
  74. {
  75. var stopwatch = _stopwatch.Value;
  76. stopwatch.Stop();
  77. Console.WriteLine($"CommandText: {command.CommandText}");
  78. Console.WriteLine($"Elapsed: {stopwatch.Elapsed}");
  79. Console.WriteLine();
  80. }
  81. }

上面的代码定义了3个观察者,第二个中将一个主题下的事件和数据写入了观察者。第三个观察者实现中,为了简化写法,使用了Microsoft.Extensions.DiagnosticAdapter 扩展。
DiagnosticName 特性定义的是侦听的事件,DiagnosticSource Write 写入时第一个参数就是事件名,第二个参数就是写入的数据。
定义一个简单的查询操作

  1. public static int Get()
  2. {
  3. using var connection = new SqlConnection(connectionString);
  4. return connection.QuerySingle<int>("SELECT 33;");
  5. }

客户端调用测试:

  1. static void Main(string[] args)
  2. {
  3. var observer = new MyObserver<DiagnosticListener>(x =>
  4. {
  5. Console.WriteLine(x.Name);
  6. });
  7. // 可以看看底层组件发布了哪些listener
  8. DiagnosticListener.AllListeners.Subscribe(observer);
  9. // 查看listener 中发布了哪些事件
  10. //DiagnosticListener.AllListeners.Subscribe(new ExampleDiagnosticObserver());
  11. // 注册sqlclient观察者
  12. //DiagnosticListener.AllListeners.Subscribe(new SqlClientObserver());
  13. Console.WriteLine(Get());
  14. Console.ReadKey();
  15. }

使用第一个观察者:

使用第二个观察者,可以发现有6个埋点事件:

使用第三个观察者记录的日志如下:

4. 使用Microsoft.Extensions.DiagnosticAdapter 实现对HttpClient的监听

要对HttpClient实现监听,我们需要先搞清埋点事件,使用上面的两个观察者查看

  1. public static int GetHttpResponse()
  2. {
  3. using var httpClient = new HttpClient();
  4. var response = httpClient.GetAsync("https://www.baidu.com").Result;
  5. return response.Content.ReadAsStringAsync().Result.Length;
  6. }
  7. static void Main(string[] args)
  8. {
  9. var observer = new MyObserver<DiagnosticListener>(x =>
  10. {
  11. Console.WriteLine(x.Name);
  12. });
  13. // 可以看看底层组件发布了哪些listener
  14. DiagnosticListener.AllListeners.Subscribe(observer);
  15. // 查看listener 中发布了哪些事件
  16. //DiagnosticListener.AllListeners.Subscribe(new ExampleDiagnosticObserver());
  17. // 注册httpclient 观察者
  18. //DiagnosticListener.AllListeners.Subscribe(new HttpClientObserver());
  19. Console.WriteLine(GetHttpResponse());
  20. Console.ReadKey();
  21. }

可以发现发布者主题是:HttpHandlerDiagnosticListener

主题事件:

使用Microsoft.Extensions.DiagnosticAdapter 编写的观察者:

  1. public class HttpClientObserver : IObserver<DiagnosticListener>
  2. {
  3. private readonly List<IDisposable> _subscriptions = new List<IDisposable>();
  4. private readonly AsyncLocal<Stopwatch> _stopwatch = new AsyncLocal<Stopwatch>();
  5. public void OnCompleted()
  6. {
  7. }
  8. public void OnError(Exception error)
  9. {
  10. }
  11. public void OnNext(DiagnosticListener value)
  12. {
  13. if (value.Name == "HttpHandlerDiagnosticListener")
  14. {
  15. var subscription = value.SubscribeWithAdapter(this);
  16. _subscriptions.Add(subscription);
  17. }
  18. }
  19. [DiagnosticName("System.Net.Http.Request")]
  20. public void HttpRequest(HttpRequestMessage request)
  21. {
  22. Console.WriteLine($"request url: {request.RequestUri.AbsoluteUri}");
  23. Console.WriteLine($"request method: {request.Method}");
  24. }
  25. [DiagnosticName("System.Net.Http.Response")]
  26. public void HttpResponse(HttpResponseMessage response)
  27. {
  28. Console.WriteLine($"response status code: {response.StatusCode}");
  29. Console.WriteLine($"response version: {response.Version}");
  30. }
  31. [DiagnosticName("System.Net.Http.Exception")]
  32. public void HttpException(HttpRequestMessage request, Exception exception)
  33. {
  34. Console.WriteLine(exception.Message);
  35. }
  36. }

记录的日志如下:

5. 总结

  • DiagnosticListener 是DiagnosticSource的具体实现,.NetCore 已经为基础类库做好了事件埋点。
  • DiagnosticListener.AllListeners 是一个外观包装类,所有Listener的观察者都可以在这里注册。

原文链接:http://www.cnblogs.com/aimigi/p/14416852.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号