Asp.Net Core轻松学之利用日志监视进行服务遥测详解

前言

在 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<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 类继承它

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

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<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. 生产事件

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 事件源生产的开始和结束连接事件

结束语

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

示例代码下载:http://xiazai.jb51.net/201812/yuanma/Ron.ListenerDemo_jb51.rar

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • ASP.NET Core开发教程之Logging利用NLog写日志文件

    前言 本文主要介绍了ASP.NET Core 开发-Logging 使用NLog 写日志文件的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 NLog 可以适用于 .NET Core 和 ASP.NET Core . ASP.NET Core已经内置了日志支持,可以轻松输出到控制台. 学习Logging 组件的相关使用,使用NLog 将日志写入到文件记录. Logging 使用 新建一个 ASP.NET Core 项目,为了方便,我选择Web 应用程序,改身份验证 改为

  • .NET Core日志配置的方法

    熟悉ASP.NET的开发者一定对web.config文件不陌生.在ASP.NET环境中,要想添加配置参数,一般也都会在此文件中操作.其中最常用的莫过于AppSettings与ConnectionStrings两项.而要在代码中获得文件中的配置信息,ConfigurationManager则是必不可少需要引入的程序集. 然而到了ASP.NET Core时代,存储与读取配置的方式都发生了改变. 如果对ASP.NET Core项目有所了解的话,应该会看到过appsettings.json这个文件.这里

  • .NET Core开发日志之OData(Open Data Protocol)

    简述 OData,即Open Data Protocol,是由微软在2007年推出的一款开放协议,旨在通过简单.标准的方式创建和使用查询式及交互式RESTful API. 类库 在.NET Core中想要使用OData功能的话需要添加Microsoft.AspNetCore.OData包. dotnet add package Microsoft.AspNetCore.OData 准备模型类 public class Address { public string City { get; set

  • 详解.Net core2.0日志组件Log4net、Nlog简单性能测试

    .Net core之Log4net.Nlog简单性能测试 比较log4net.nlog的文件写入性能(.netcore环境),涉及代码和配置如有不正确的地方,还请批评指正. 测试环境 开发工具: Vsual Studio 2017 15.3 框架版本: .net core 2.0 操作系统:window10 Enterprise 1703 硬件配置:CPU I3-4170 3.7GHz,内存 8G,固态硬盘 日志组件 log4net 2.0.8 nlog 5.0.0-beta10 测试用例 1.

  • 详解ASP.NET Core应用中如何记录和查看日志

    日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.LoggerFactory和LoggerProvider这三个核心对象组成.我们可以通过简单的配置实现对LoggerFactory的定制,以及对LoggerProvider添加. 一. 配置LoggerFactory 我们在上面一节演示了一个展示ASP.NET Core默认注册服务的实例,细心的读者一定会看到显

  • .Net Core项目如何添加日志功能详解

    一.微软内置的日志组件 在.Net Core中使用模板新建的Web Api项目时,会自动加入日志功能.只需要在控制器中注入ILogger就可以了.命名空间为:Microsoft.Extensions.Logging. 会发现只有Error被打印到了控制台,Trace没有被打印.那是因为在appsetting.json中配置了Logging>Console>Default的等级为Debug,日志的等级大于等于Debug才会输出到控制台.在这里说一下LogLevel:Trace<Debug&

  • 详解.Net Core中的日志组件(Logging)

    1.介绍 Logging组件是微软实现的日志记录组件包括控制台(Console).调试(Debug).事件日志(EventLog)和TraceSource,但是没有实现最常用用的文件记录日志功能(可以用其他第三方的如NLog.Log4Net.之前写过NLog使用的文章). 2.默认配置 新建.Net Core Web Api项目,添加下面代码. [Route("api/[controller]")] public class ValuesController : Controller

  • Asp.Net Core轻松学之利用日志监视进行服务遥测详解

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

  • ASP.NET Core扩展库之Http通用扩展库的使用详解

    本文将介绍Xfrogcn.AspNetCore.Extensions扩展库对于Http相关的其他功能扩展,这些功能旨在处理一些常见需求, 包括请求缓冲.请求头传递.请求头日志范围.针对HttpClient与HttpRequestMessage.HttpResponseMessage的扩展方法. 一.开启服务端请求缓冲 ASP.NET Core 中请求体是不能多次读取的,由于在MVC中,框架已经读取过请求体,如果你在控制器中再次读取,将会引发异常,如下示例: [ApiController] [Ro

  • ASP.NET Core 6最小API中使用日志和DI示例详解

    目录 在ASP.NET Core 6的最小API中使用日志和DI 如何在ASP.NET Core 6的最小API中实现日志.从配置系统中读取并使用依赖注入 CI/CD?持续集成和持续交付解释 在Visual Studio 2022中创建一个ASP.NET Core minimal web API项目 运行一个最小的网络API 为一个最小的网络API配置多个端口 在最小的Web API中使用日志记录 在最小的API中从配置系统中读取 在最小的网络API中使用依赖性注入 在一个最小的Web API中

  • 如何在ASP.NET Core应用程序运行Vue并且部署在IIS上详解

    前言 从.NET Core 1.0开始我们就将其应用到项目中,但是呢我对ASP.NET Core一些原理也还未开始研究,仅限于会用,不过园子中已有大量文章存在,借着有点空余时间,我们来讲讲如何利用ASP.NET Core结合Vue在IIS上运行. ASP.NET Core结合Vue部署于IIS 关于安装Vue和Webpack则不再叙述,我们直接来创建ASP.NET Core应用程序或者通过dotnet new mvc创建ASP.NET Core应用程序 接下来在上述应用程序下通过如下命令创建Vu

  • ASP.NET Core中调整HTTP请求大小的几种方法详解

    一.前言 之所以称ASP.NET Core是一个Web开发平台,源于它具有一个极具扩展性的请求处理管道,我们可以通过这个管道的定制来满足各种场景下的HTTP处理需求.ASP. NET Core应用的很多特性,比如路由.认证.会话.缓存等,也同时定制消息处理管道来实现的.我们甚至可以通过管道定制在ASP.NET Core平台上创建我们自己的Web框架,实际上MVC和SingalR这两个重要的Web框架也是采用这样的方式创建的. HTTP协议自身的特性决定了任何一个Web应用的工作方式都是监听.接收

  • ASP.NET Core针对一个使用HttpClient对象的类编写单元测试详解

    介绍 几年前,微软引入了HttpClient类来替代HttpWebRequest来发送Web请求.这个新的类更易于使用,更加简洁,更具有异步性,且易于扩展. HttpClient类有一个可以接受HttpMessageHandler类对象的构造函数.HttpMessageHandler类对象可以接受一个请求(HttpRequestMessage), 并返回响应(HttpResponseMessage).它的功能完全取决于它的实现.默认情况下HttpClient使用的是HttpClientHandl

  • ASP.NET Core对不同类型的用户进行区别限流详解

    前言 老板提出了一个新需求,从某某天起,免费用户每天只能查询100次,收费用户100W次. 这是一个限流问题,聪明的你也一定想到了如何去做:记录用户每一天的查询次数,然后根据当前用户的类型使用不同的数字做比较,超过指定的数字就返回错误. 嗯,原理就是这么简单.不过真正写起来还要考虑更多问题: 统计数据的数据结构是什么样的?字典 or 行记录? 统计数据记录到哪里?内存 or MySQL or Redis? 分布式应用怎么精确计数?分布式锁 or 队列 or 事务? 吞吐量比较大时如何扛得住?内存

  • ASP.NET Core扩展库之Http日志的使用详解

    最佳实践都告诉我们不要记录请求的详细日志,因为这有安全问题,但在实际开发中,请求的详细内容对于快速定位问题却是非常重要的,有时也是系统的强力证据.Xfrogcn.AspNetCore.Extensions扩展库提供了服务端和客户端的详细日志功能,通过配置可以开启. 服务端日志通过请求中间件来完成,中间件会以Trace级别记录请求和应答详情,以Debug级别记录请求耗时.服务的请求日志的名称为ServerRequest.Logger 要开启服务端详情日志,只需将扩展库配置中的ServerReque

  • .NET Core利用 AsyncLocal 实现共享变量的代码详解

    目录 简介 AsyncLocal 解读 总结 简介 我们如果需要整个程序共享一个变量,我们仅需将该变量放在某个静态类的静态变量上即可(不满足我们的需求,静态变量上,整个程序都是固定值).我们在Web 应用程序中,每个Web 请求服务器都为其分配了一个独立线程,如何实现用户,租户等信息隔离在这些独立线程中.这就是今天要说的线程本地存储.针对线程本地存储 .NET 给我们提供了两个类 ThreadLocal 和 AsyncLocal.我们可以通过查看以下例子清晰的看到两者的区别: [TestClas

  • 利用二进制文件安装etcd的教程详解

    etcd组件作为一个高可用强一致性的服务发现存储仓库. etcd作为一个受到ZooKeeper与doozer启发而催生的项目,除了拥有与之类似的功能外,更专注于以下四点. 简单:基于HTTP+JSON的API让你用curl就可以轻松使用. 安全:可选SSL客户认证机制. 快速:每个实例每秒支持一千次写操作. 可信:使用Raft算法充分实现了分布式. 场景一:服务发现(Service Discovery)一个强一致性.高可用的服务存储目录.基于Raft算法的etcd天生就是这样一个强一致性高可用的

随机推荐