加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

如何利用ETW(Event Tracing for Windows)记录日志

发布时间:2020-12-16 09:08:55 所属栏目:asp.Net 来源:网络整理
导读:ETW是Event Tracing for Windows的简称,它是Windows提供的原生的事件跟踪日志系统。由于采用内核(Kernel)层面的缓冲和日志记录机制,所以ETW提供了一种非常高效的事件跟踪日志解决方案。 一、ETW模型 事件监测(Event Instrumentation)总会包含两个基本

ETW是Event Tracing for Windows的简称,它是Windows提供的原生的事件跟踪日志系统。由于采用内核(Kernel)层面的缓冲和日志记录机制,所以ETW提供了一种非常高效的事件跟踪日志解决方案。

一、ETW模型

事件监测(Event Instrumentation)总会包含两个基本的实体,事件的提供者(ETW Provider)和消费者(ETW Consumer),ETW框架可以视为它们的中介。ETW Provider会预先注册到ETW框架上,提供者程序在某个时刻触发事件,并将标准化定义的事件提供给ETW框架。Consumer同样需要注册到ETW框架上,在注册的时候可以设置事件的删选条件和接收处理事件的回掉。对于接收到的事件,如果它满足某个注册ETW Consumer的筛选条件,ETW会调用相应的回调来处理该事件。

ETW针对事件的处理是在某个会话(ETW Session)中进行的,ETW Session提供了一个接收、存储、处理和分发事件的执行上下文。ETW框架可以创建多一个会话来处理由提供者程序发送的事件,但是ETW Session并不会与某个单一的提供者绑定在一起,多个提供者程序可以向同一个ETW Session发送事件。对于接收到的事件,ETW Session可以将它保存在创建的日志文件中,也可以实时地分发给注册的消费者应用。ETW会话的开启和终止是通过 Session的开启和终止是通过ETW控制器(ETW Controller)进行管理的。除了管理ETW Session之外,ETW Controller还可以禁用(Disable)或者恢复(Enable)注册到某个ETW Session上的ETW Provider。

综上所述,整个ETW模型由ETW框架本身和ETW Provider、ETW Consumer以及ETW Controller组成,上图很好地展示了这四者之间的关系。出于篇幅的限制,我们只能对ETW作一个粗略的介绍,实际上ETW自身是一个非常强大的事件跟踪日志系统,有兴趣的朋友可以参阅相关的文档进行系统学习。

二、针对ETW的编程

.NET提供了一种比较独特并且简单的编程模式编写针对ETW的事件提供和消费程序。所谓的ETW Provider就是事件的提供者,它体现了事件的来源,对应着一个EventSource对象。EventSource类型定义在“System.Diagnostics.Tracing”这个NuGet包中。如下面的代码片段所示,EventSource类型中定义了一系列WriteEvent方法重载,这些方法会触发一个事件并将其递交给ETW框架。这些方法具有一个代表事件ID的参数,必须是一个介于[0,65535] 之间的整数,而其他参数将作为负载(Payload)附加到事件对象上。

   1: public class EventSource : IDisposable
   2: {   
   3:     ...
   4:     protected void WriteEvent(int eventId);    
   5:     int eventId,int arg1);    
   6:     long arg1);    
   7:     string arg1);    
   8:     byte[] arg1);    
   9:     params object[] args);    
  10:     int arg1,1)">int arg2);    
  11:     string arg2);    
  12:     long arg1,1)">long arg2);    
  13:       14:     byte[] arg2);    
  15:     string arg1,1)'>  16:       17:       18:     int arg2,1)">int arg3);    
  19:     long arg2,1)">long arg3);    
  20:       21:     string arg2,1)">string arg3);
  22: }

由于其中一个WriteEvent方法具有一个通过params关键字定义的参数,并且参数的类型为对象数组,所以我们可以在编程的时候可以将任意类型和不限数量的对象作为参数,但实际上作为负载的这些参数只能是原生类型(Primative Type)、字符串、GUID和枚举这些简单的类型而已。其实EventSource类型还具有很多其他成员,但是篇幅所限,我们就不对它详细介绍了。

虽然EventSource并不是一个抽象类,但是我们在具体项目开发中都不会直接创建一个EventSource对象并调用它的WriteEvent方法触发并向ETW框架一个事件。一般来说,我们会为一组相关的事件定义一个继承于EventSource的子类,比如我们定义了如下一个SecurityEventSource的类型来写入与安全相关的事件。

2: class SecurityEventSource : EventSource
   4:     ...
static readonly SecurityEventSource Instance = new SecurityEventSource();
   1: [AttributeUsage(AttributeTargets.Class)]
string Guid { get; set; }
string LocalizationResources { get; set; }  
   1: [AttributeUsage(AttributeTargets.Method)]
   3: {}
   5: [AttributeUsage(AttributeTargets.Method)]
   7: {  
   9:? 
public EventLevel               Level { get;  set; }     
public EventKeywords            Keywords { get; set; }   
public EventOpcode              Opcode { get; set; }  
public EventChannel             Channel { get; set; }  
public EventActivityOptions     ActivityOptions { get;  set; } 
enum EventLevel
   3:     LogAlways,
   9: }

EventAttribute的Keywords和EventTags属性分别代表为事件指定的关键字和标签,它的Opcode和Task属性帮助我们将事件映射为一个预定义的操作和任务。这四个属性的返回类型都是枚举,但是实际上在很多情况下我们都是将它们当作一个整数在用。举个简单的例子,表示操作代码的EventOpcode枚举中定义了如下11个选项,并且每个选项都具有一个具体的值,但是我们可以完全忽略它们。我们的应用完全可以按照自己的标准来定义操作代码,我们可以将整数1映射任何一个操作(比如“登录”),虽然它在EventOpcode中表示“开启(Start)”。

3: Info = 0,1)"> 4: Start = 1,1)"> 5: Stop = 2,1)"> 6: DataCollectionStart = 3,1)"> 7: DataCollectionStop = 4,1)"> 8: Extension = 5,1)"> 9: Reply = 6,1)"> 10: Resume = 7,1)"> 11: Suspend = 8,1)"> 12: Send = 9,1)"> 13: Receive = 240,1)"> 14: }

ETW Provider提供的事件还可以写入Event Log,EventAttribute的Channel属性用于设置Event Log通道(Event Log Channel,可以理解为事件从最初的发布到最终被写入日志文件所经历的传输通道)的类型,具体的通道类型定义在如下一个EventChannel枚举中。如果时间具有版本,我们将版本号定义在EventAttribute的Version属性上。

2: {
   8: }

我们可以利用ETW事件来追踪(Track)某个活动(Activity)的开始和终止,而活动开始与终止的追踪行为可以通过EventAttribute的ActivityOptions属性来设置。这个属性对应的EventActivityOptions为我们定义了四种活动追踪行为,其中None表示默认行为,而Disable则意味着禁止追踪。Recursive表示可以“递归”的方式开启和终止活动,比如“开始A”-“开始B”-“终止B”-“终止A”。另一个选项Detachable表示多个活动之间可以有重叠,比如“开始A”-“开始B”-“终止A”-“终止B”。

enum EventActivityOptions
   1: [EventSource(Name =class Keywords
   6:         const EventKeywords Authentication = (EventKeywords)1;
   8:     }
  11:     {}
  14:? 
void SignInSuccess(string userName)
  18:         this.WriteEvent(1,userName);
  20:? 
  22:     void SignInFail(  23:     {
  24:         this.WriteEvent(2,1)'>  25:     }
  26:? 
  27:     [Event(3,Keywords = Keywords.Authorization,1)">"用户{0}执行未授权操作{1}")]
  28:     void DenyAccess(string userName,1)">string operation)
  29:     {
  30:         this.WriteEvent(3,userName,operation);
  31:     }
  32: }

定义在SecurityEventSource中的三个实例方法对应着三个ETW事件,其中方法SignInSuccess和SignInFail与认证相关,表示认证成功和认证失败。通过标注的EventAttribute特性可以看出,这两个ETW事件采用的等级分别为Informational和Warning。我们为这个特性的Message属性设置了一个包含占位符的模板,占位符代表作为参数传入的用户名。另一个方法DenyAccess与授权相关,表示某个用户试图执行一个未被授权的操作,我们依然通过标注的EventAttribute事件设置了ETW事件的关键字、等级和消息。我们最终在Main方法中按照如下的方式调用这三个方法。