博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Asp.Net Core 轻松学-利用日志监视进行服务遥测
阅读量:6520 次
发布时间:2019-06-24

本文共 7992 字,大约阅读时间需要 26 分钟。

原文:

前言

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

1. EventListener 介绍

1.1 EventListener 中文直译为:事件侦听器

EventListener 位于程序集 System.Diagnostics.Tracing 中,该类提供了一组启用/禁用的方法,按照惯例,先来看一下源代码,了解一下其结构

public abstract class EventListener : IDisposable    {        protected EventListener();        public event EventHandler
EventSourceCreated; public event EventHandler
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 类继承它

2. 创建自定义事件侦听器
public class ReportListener : EventListener    {        public ReportListener() { }        public Dictionary
Items { get; set; } = new Dictionary
(); public ReportListener(Dictionary
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 的跟踪配置集,通过配置文件注入,动态觉得哪些事件可以被写入到侦听器中

3. 配置跟踪项目

在配置文件 appsettings.json 中增加以下内容

{  "listener": [    {      "name": "HomeEventSource",      "level": 5,      "keywords": -1    }  ]}

配置说明

上面的配置文件表示,定义一个事件源对象(EventSource),名称为 HomeEventSource,事件级别(EventLevel)为 5,关键字(EventKeywords)为 -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
>(); Dictionary
dict = new Dictionary
(); if (listeners != null) { foreach (var item in listeners) { dict.Add(item.Name, item); } } var report = new ReportListener(dict); services.AddSingleton
(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. 生产事件

5.1 转到 HomeController,在 HomeController 的 Get 方法中使用 HomeEventSource 生产两个事件

[Route("api/[controller]")]    [ApiController]    public class HomeController : ControllerBase    {        [HttpGet]        public ActionResult
> 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 运行程序,看看输出结果如何

26882-20181216220042721-288328064.png

可以看到,事件生产成功,实际上,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 再次运行程序,看下图输出结果

26882-20181216220055031-429906628.png

从图中可以看出,这次我们跟踪到了 Microsoft-AspNetCore-Server-Kestrel 事件源生产的开始和结束连接事件

结束语

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

示例代码下载

转载地址:http://zeubo.baihongyu.com/

你可能感兴趣的文章
《Android游戏开发详解》——第1章,第1.3节声明和初始化变量
查看>>
《Visual Studio程序员箴言》----1.2 滚动与导航
查看>>
Processing编程学习指南2.7 Processing参考文档
查看>>
架构师速成-架构目标之伸缩性\安全性
查看>>
执行可运行jar包时读取jar包中的文件
查看>>
linux下ExtMail邮件使用及管理平台
查看>>
linux中iptables设置自建dns服务器的端口
查看>>
TP5+PHPexcel导入xls,xlsx文件读取数据
查看>>
基于Yum安装zabbix3.0
查看>>
Master-work模式
查看>>
dos命令行 指令
查看>>
RT-Thread--时间管理
查看>>
BUPT 63T 高才生 找最佳基站
查看>>
linux 学习(二)防火墙
查看>>
scala001
查看>>
【实习记】2014-08-20实习的mini项目总结
查看>>
android - SpannableString或SpannableStringBuilder以及string.xml文件中的整型和string型代替...
查看>>
自己选择的路,跪着走完吧——一个兔纸的话
查看>>
zabbix-3.2.3+php-5.6.29+percona-server-5.6.29-76.2+nginx-1.10.2(CentOS6.8)
查看>>
三端稳压器各个参数解释
查看>>