前言

    在 Net Core 2.2 中,官方文档表示,对 EventListener 这个日志监视类的内容进行了扩充,同时赋予了跟踪 CoreCLR 事件的权限;通过跟踪 CoreCLR 事件,比如通过跟踪 CoreCLR 事件,可以了解和收集到比如 GC,JIT,ThreadPool,intreop 这些运行时服务的行为;通过使用配置注入,我们将获得一种动态跟踪事件的能力。

1. EventListener 介绍

1.1 EventListener 中文直译为:事件侦听器
    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);
    }
2. 创建自定义事件侦听器
 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");
            }
        }
    }
3. 配置跟踪项目
{
  "listener": [
    {
      "name": "HomeEventSource",
      "level": 5,
      "keywords": -1
    }
  ]
}

3.1 事件级别定义

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 配置文件完全按照系统值定义,为了更好的使用配置文件,我们定义了下面的实体类

    public class ListenerItem
    {
        public string Name { get; set; }
        public EventLevel Level { get; set; } = EventLevel.Verbose;
        public EventKeywords Keywords { get; set; } = EventKeywords.All;
    }
4. 开始使用事件侦听器

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

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);
    }
5. 生产事件

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

5.3 运行程序,看看输出结果如何

Asp.Net Core 轻松学-利用日志监视进行服务遥测-LMLPHP

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 再次运行程序,看下图输出结果

Asp.Net Core 轻松学-利用日志监视进行服务遥测-LMLPHP

结束语

  • 在 CoreCLR 的事件总线中,包含了千千万万的事件源生产的事件,以上的实验只是冰山一角,如果你把创建事件源的 EventKeywords 指定为 All,你将会看到天量的日志信息,但是,在这里,友情提示大家,千万不要这样做,这种做法会对服务性能带来极大损害
  • 在业务代码中,写入大量的调试日志是不可取的,但是使用事件侦听器,可以控制事件的创建和写入,当需要对某个接口进行监控的时候,通过将需要调试的事件源加入配置文件中进行监控,这将非常有用

示例代码下载

https://files.cnblogs.com/files/viter/Ron.ListenerDemo.zip

12-17 11:06