Asp.Net Core輕松學(xué)之利用日志監(jiān)視進(jìn)行服務(wù)遙測(cè)詳解
前言
在 Net Core 2.2 中,官方文檔表示,對(duì) EventListener 這個(gè)日志監(jiān)視類(lèi)的內(nèi)容進(jìn)行了擴(kuò)充,同時(shí)賦予了跟蹤 CoreCLR 事件的權(quán)限;通過(guò)跟蹤 CoreCLR 事件,比如通過(guò)跟蹤 CoreCLR 事件,可以了解和收集到比如 GC,JIT,ThreadPool,intreop 這些運(yùn)行時(shí)服務(wù)的行為;通過(guò)使用配置注入,我們將獲得一種動(dòng)態(tài)跟蹤事件的能力。
1. EventListener 介紹
1.1 EventListener 中文直譯為:事件偵聽(tīng)器
EventListener 位于程序集 System.Diagnostics.Tracing
中,該類(lèi)提供了一組啟用/禁用的方法,按照慣例,先來(lái)看一下源代碼,了解一下其結(jié)構(gòu)
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); }
從類(lèi)結(jié)構(gòu)中可以看出,EventListener 中的方法并不多,而且從名字都可以推斷出其行為,
因?yàn)樵擃?lèi)是一個(gè)抽象類(lèi),并不能直接使用,接下來(lái)我們創(chuàng)建一個(gè) ReportListener 類(lèi)繼承它
2. 創(chuàng)建自定義事件偵聽(tīng)器
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 自定義事件偵聽(tīng)器的代碼非常簡(jiǎn)單,只是簡(jiǎn)單的繼承了 EventListener 后,重寫(xiě)了父類(lèi)的兩個(gè)方法:創(chuàng)建事件和寫(xiě)入事件
同時(shí),還定義了一個(gè)公共屬性 Dictionary<string, ListenerItem> Items
,該屬性接受一個(gè) ListenerItem 的跟蹤配置集,通過(guò)配置文件注入,動(dòng)態(tài)覺(jué)得哪些事件可以被寫(xiě)入到偵聽(tīng)器中
3. 配置跟蹤項(xiàng)目
在配置文件 appsettings.json 中增加以下內(nèi)容
{ "listener": [ { "name": "HomeEventSource", "level": 5, "keywords": -1 } ] }
配置說(shuō)明
上面的配置文件表示,定義一個(gè)事件源對(duì)象(EventSource),名稱為 HomeEventSource,事件級(jí)別(EventLevel)為 5,關(guān)鍵字(EventKeywords)為 -1
關(guān)于事件級(jí)別和事件關(guān)鍵字的值,和系統(tǒng)定義的一致
3.1 事件級(jí)別定義
namespace System.Diagnostics.Tracing { public enum EventLevel { LogAlways = 0, Critical = 1, Error = 2, Warning = 3, Informational = 4, Verbose = 5 } }
3.2 事件關(guān)鍵字定義
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 配置文件完全按照系統(tǒng)值定義,為了更好的使用配置文件,我們定義了下面的實(shí)體類(lèi)
public class ListenerItem { public string Name { get; set; } public EventLevel Level { get; set; } = EventLevel.Verbose; public EventKeywords Keywords { get; set; } = EventKeywords.All; }
4. 開(kāi)始使用事件偵聽(tīng)器
為了在應(yīng)用程序中使用事件偵聽(tīng)器,我們需要初始化事件偵聽(tīng)器,你可以初始化多個(gè)事件偵聽(tīng)器;但是,每個(gè)事件偵聽(tīng)器僅需要初始化一次即可
4.1 初始化自定義事件偵聽(tīng)器,在 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); }
初始化動(dòng)作非常簡(jiǎn)單,僅是從配置文件中讀取需要跟蹤的項(xiàng),然后注冊(cè)到 ReportListener 內(nèi)部即可,為了演示事件的注冊(cè),我們需要?jiǎng)?chuàng)建一個(gè)事件源,就像配置文件中的名稱 HomeEventSource
4.2 創(chuàng)建自定義的事件源對(duì)象
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,我們可無(wú)需為該自定義事件源進(jìn)行顯式命名,因?yàn)槟J(rèn)將會(huì)使用 HomeEventSource 類(lèi)名進(jìn)行注冊(cè)事件
現(xiàn)在,我們嘗試著 HomeController 去生產(chǎn)一個(gè)事件,看看效果
5. 生產(chǎn)事件
5.1 轉(zhuǎn)到 HomeController,在 HomeController 的 Get 方法中使用 HomeEventSource 生產(chǎn)兩個(gè)事件
[Route("api/[controller]")] [ApiController] public class HomeController : ControllerBase { [HttpGet] public ActionResult<IEnumerable<string>> Get() { HomeEventSource.Instance.RequestStart("處理業(yè)務(wù)開(kāi)始"); var arra = new string[] { "value1", "value2" }; HomeEventSource.Instance.RequestStop("處理業(yè)務(wù)結(jié)束"); return arra; } }
5.2 回顧一下自定義事件偵聽(tīng)器 ReportListener 的重寫(xiě)方法
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 事件源才啟用事件,所以上面的代碼表示,當(dāng)一個(gè) HomeEventSource 事件進(jìn)入的時(shí)候,將事件的內(nèi)容打印到控制臺(tái),實(shí)際應(yīng)用中,你可以將這些信息推送到日志訂閱服務(wù)器,以方便跟蹤和匯總
5.3 運(yùn)行程序,看看輸出結(jié)果如何
可以看到,事件生產(chǎn)成功,實(shí)際上,CoreCLR 內(nèi)部生產(chǎn)了非常多的事件,下面我們嘗試啟用以下 3 個(gè)事件源,預(yù)期將會(huì)收到大量的事件信息
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 再次運(yùn)行程序,看下圖輸出結(jié)果
從圖中可以看出,這次我們跟蹤到了 Microsoft-AspNetCore-Server-Kestrel 事件源生產(chǎn)的開(kāi)始和結(jié)束連接事件
結(jié)束語(yǔ)
- 在 CoreCLR 的事件總線中,包含了千千萬(wàn)萬(wàn)的事件源生產(chǎn)的事件,以上的實(shí)驗(yàn)只是冰山一角,如果你把創(chuàng)建事件源的 EventKeywords 指定為 All,你將會(huì)看到天量的日志信息,但是,在這里,友情提示大家,千萬(wàn)不要這樣做,這種做法會(huì)對(duì)服務(wù)性能帶來(lái)極大損害
- 在業(yè)務(wù)代碼中,寫(xiě)入大量的調(diào)試日志是不可取的,但是使用事件偵聽(tīng)器,可以控制事件的創(chuàng)建和寫(xiě)入,當(dāng)需要對(duì)某個(gè)接口進(jìn)行監(jiān)控的時(shí)候,通過(guò)將需要調(diào)試的事件源加入配置文件中進(jìn)行監(jiān)控,這將非常有用
示例代碼下載:http://xiazai.jb51.net/201812/yuanma/Ron.ListenerDemo_jb51.rar
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- ASP.NET?Core記錄日志
- ASP.Net?Core中的日志與分布式鏈路追蹤
- ASP.NET Core使用NLog記錄日志
- ASP.NET Core使用Log4net實(shí)現(xiàn)日志記錄功能
- ASP.NET Core使用NLog輸出日志記錄
- ASP.NET Core擴(kuò)展庫(kù)之Http日志的使用詳解
- ASP.NET Core擴(kuò)展庫(kù)之日志功能的使用詳解
- Asp.Net Core用NLog記錄日志操作方法
- ASP.NET Core開(kāi)發(fā)教程之Logging利用NLog寫(xiě)日志文件
- ASP.NET Core 2.0 WebApi全局配置及日志實(shí)例
- 詳解ASP.NET Core應(yīng)用中如何記錄和查看日志
- ASP.NET?Core使用自定義日志中間件
相關(guān)文章
詳解ASP.NET 生成二維碼實(shí)例(采用ThoughtWorks.QRCode和QrCode.Net兩種方式)
本篇文章主要介紹了ASP.NET 生成二維碼實(shí)例,使用了兩種方法,包括ThoughtWorks.QRCode和QrCode.Net,具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12淺談ASP.NET Core 中jwt授權(quán)認(rèn)證的流程原理
這篇文章主要介紹了淺談ASP.NET Core 中jwt授權(quán)認(rèn)證的流程原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03.NET下實(shí)現(xiàn)數(shù)字和字符相混合的驗(yàn)證碼實(shí)例
這篇文章介紹了.NET下實(shí)現(xiàn)數(shù)字和字符相混合的驗(yàn)證碼實(shí)例,有需要的朋友可以參考一下2013-11-11.NET實(shí)現(xiàn)ChatGPT的Stream傳輸?shù)倪^(guò)程
這篇文章主要介紹了.NET如何實(shí)現(xiàn)ChatGPT的Stream傳輸,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07asp.net調(diào)用飛信免費(fèi)發(fā)短信(測(cè)試有效)
這篇文章主要介紹了asp.net如何調(diào)用飛信免費(fèi)發(fā)短信,記得要開(kāi)通飛信把對(duì)方加為好友才能發(fā),需要的朋友可以參考下2014-05-05.NET從優(yōu)酷專輯中采集所有視頻及信息(VB.NET代碼)
因?yàn)橄胱鲆粋€(gè)視頻點(diǎn)播類(lèi)的網(wǎng)站,所以開(kāi)始研究視頻采集。2010-02-02asp.net core 3.0中使用swagger的方法與問(wèn)題
這篇文章主要給大家介紹了關(guān)于asp.net core 3.0中使用swagger的方法與遇到的一些問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用asp.net core 3.0具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10