在 Net Core 2.2 中,官方文檔表示,對 EventListener 這個日誌監視類的內容進行了擴充,同時賦予了跟蹤 CoreCLR 事件的權限;經過跟蹤 CoreCLR 事件,好比經過跟蹤 CoreCLR 事件,能夠了解和收集到好比 GC,JIT,ThreadPool,intreop 這些運行時服務的行爲;經過使用配置注入,咱們將得到一種動態跟蹤事件的能力。git
EventListener 位於程序集 System.Diagnostics.Tracing 中,該類提供了一組啓用/禁用的方法,按照慣例,先來看一下源代碼,瞭解一下其結構github
public abstract class EventListener : IDisposable { protected EventListener(); public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated; public event EventHandler<EventWrittenEventArgs> EventWritten; protected static int EventSourceIndex(EventSource eventSource); public void DisableEvents(EventSource eventSource); public virtual void Dispose(); public void EnableEvents(EventSource eventSource, EventLevel level); public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword); protected internal virtual void OnEventWritten(EventWrittenEventArgs eventData); }
從類結構中能夠看出,EventListener 中的方法並很少,並且從名字均可以推斷出其行爲,
由於該類是一個抽象類,並不能直接使用,接下來咱們建立一個 ReportListener 類繼承它json
public class ReportListener : EventListener { public ReportListener() { } public Dictionary<string, ListenerItem> Items { get; set; } = new Dictionary<string, ListenerItem>(); public ReportListener(Dictionary<string, ListenerItem> items) { this.Items = items; } protected override void OnEventSourceCreated(EventSource eventSource) { if (Items.ContainsKey(eventSource.Name)) { var item = Items[eventSource.Name]; EnableEvents(eventSource, item.Level, item.Keywords); } } protected override void OnEventWritten(EventWrittenEventArgs eventData) { if (Items.ContainsKey(eventData.EventSource.Name)) { Console.WriteLine($"ThreadID = {eventData.OSThreadId} ID = {eventData.EventId} Name = {eventData.EventSource.Name}.{eventData.EventName}"); for (int i = 0; i < eventData.Payload.Count; i++) { string payloadString = eventData.Payload[i]?.ToString() ?? string.Empty; Console.WriteLine($"\tName = \"{eventData.PayloadNames[i]}\" Value = \"{payloadString}\""); } Console.WriteLine("\n"); } } }
ReportListener 自定義事件偵聽器的代碼很是簡單,只是簡單的繼承了 EventListener 後,重寫了父類的兩個方法:建立事件和寫入事件
同時,還定義了一個公共屬性 Dictionary<string, ListenerItem> Items ,該屬性接受一個 ListenerItem 的跟蹤配置集,經過配置文件注入,動態以爲哪些事件能夠被寫入到偵聽器中api
在配置文件 appsettings.json 中增長如下內容服務器
{ "listener": [ { "name": "HomeEventSource", "level": 5, "keywords": -1 } ] }
配置說明
上面的配置文件表示,定義一個事件源對象(EventSource),名稱爲 HomeEventSource,事件級別(EventLevel)爲 5,關鍵字(EventKeywords)爲 -1
關於事件級別和事件關鍵字的值,和系統定義的一致app
3.1 事件級別定義ide
namespace System.Diagnostics.Tracing { public enum EventLevel { LogAlways = 0, Critical = 1, Error = 2, Warning = 3, Informational = 4, Verbose = 5 } }
3.2 事件關鍵字定義性能
namespace System.Diagnostics.Tracing { [Flags] public enum EventKeywords : long { All = -1, None = 0, WdiContext = 562949953421312, MicrosoftTelemetry = 562949953421312, WdiDiagnostic = 1125899906842624, Sqm = 2251799813685248, AuditFailure = 4503599627370496, CorrelationHint = 4503599627370496, AuditSuccess = 9007199254740992, EventLogClassic = 36028797018963968 } }
3.3 配置文件徹底按照系統值定義,爲了更好的使用配置文件,咱們定義了下面的實體類this
public class ListenerItem { public string Name { get; set; } public EventLevel Level { get; set; } = EventLevel.Verbose; public EventKeywords Keywords { get; set; } = EventKeywords.All; }
爲了在應用程序中使用事件偵聽器,咱們須要初始化事件偵聽器,你能夠初始化多個事件偵聽器;可是,每一個事件偵聽器僅須要初始化一次便可spa
4.1 初始化自定義事件偵聽器,在 Startup.cs 文件中加入如下代碼
public void AddEventListener(IServiceCollection services) { var listeners = this.Configuration.GetSection("listener").Get<List<ListenerItem>>(); Dictionary<string, ListenerItem> dict = new Dictionary<string, ListenerItem>(); if (listeners != null) { foreach (var item in listeners) { dict.Add(item.Name, item); } } var report = new ReportListener(dict); services.AddSingleton<ReportListener>(report); } public void ConfigureServices(IServiceCollection services) { AddEventListener(services); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
初始化動做很是簡單,僅是從配置文件中讀取須要跟蹤的項,而後註冊到 ReportListener 內部便可,爲了演示事件的註冊,咱們須要建立一個事件源,就像配置文件中的名稱 HomeEventSource
4.2 建立自定義的事件源對象
public class HomeEventSource : EventSource { public static HomeEventSource Instance = new HomeEventSource(); [Event(1001)] public void RequestStart(string message) => WriteEvent(1001, message); [Event(1002)] public void RequestStop(string message) => WriteEvent(1002, message); }
自定義事件源 HomeEventSource 繼承自 EventSource,咱們可無需爲該自定義事件源進行顯式命名,由於默認將會使用 HomeEventSource 類名進行註冊事件
如今,咱們嘗試着 HomeController 去生產一個事件,看看效果
5.1 轉到 HomeController,在 HomeController 的 Get 方法中使用 HomeEventSource 生產兩個事件
[Route("api/[controller]")] [ApiController] public class HomeController : ControllerBase { [HttpGet] public ActionResult<IEnumerable<string>> Get() { HomeEventSource.Instance.RequestStart("處理業務開始"); var arra = new string[] { "value1", "value2" }; HomeEventSource.Instance.RequestStop("處理業務結束"); return arra; } }
5.2 回顧一下自定義事件偵聽器 ReportListener 的重寫方法
protected override void OnEventSourceCreated(EventSource eventSource) { if (Items.ContainsKey(eventSource.Name)) { var item = Items[eventSource.Name]; EnableEvents(eventSource, item.Level, item.Keywords); } } protected override void OnEventWritten(EventWrittenEventArgs eventData) { if (Items.ContainsKey(eventData.EventSource.Name)) { Console.WriteLine($"ThreadID = {eventData.OSThreadId} ID = {eventData.EventId} Name = {eventData.EventSource.Name}.{eventData.EventName}"); for (int i = 0; i < eventData.Payload.Count; i++) { string payloadString = eventData.Payload[i]?.ToString() ?? string.Empty; Console.WriteLine($"\tName = \"{eventData.PayloadNames[i]}\" Value = \"{payloadString}\""); } Console.WriteLine("\n"); } }
因爲咱們作配置文件中指定了必須是 HomeEventSource 事件源才啓用事件,因此上面的代碼表示,當一個 HomeEventSource 事件進入的時候,將事件的內容打印到控制檯,實際應用中,你能夠將這些信息推送到日誌訂閱服務器,以方便跟蹤和彙總
5.3 運行程序,看看輸出結果如何
能夠看到,事件生產成功,實際上,CoreCLR 內部生產了很是多的事件,下面咱們嘗試啓用如下 3 個事件源,預期將會收到大量的事件信息
5.4 嘗試更多事件源
protected override void OnEventSourceCreated(EventSource eventSource) { if (eventSource.Name.Equals("Microsoft-Windows-DotNETRuntime")) { EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.AuditFailure); } else if (eventSource.Name.Equals("System.Data.DataCommonEventSource")) { EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.AuditFailure); } else if (eventSource.Name.Equals("Microsoft-AspNetCore-Server-Kestrel")) { EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.AuditFailure); } }
5.5 再次運行程序,看下圖輸出結果
從圖中能夠看出,此次咱們跟蹤到了 Microsoft-AspNetCore-Server-Kestrel 事件源生產的開始和結束鏈接事件
https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.ListenerDemo