C# ETW事件跟踪方法 C#如何使用EventSource和EventListener

3次阅读

Eventsource 必须显式指定 name 特性(如 [eventsource(name = “mycompany.myapp”)]),确保 provider 名匹配且符合 etw 规范;监听需调用 enableevents 并核对 name、level、keywords;避免大对象传参、耗时计算及缓冲区满导致静默丢事件

C# ETW事件跟踪方法 C#如何使用EventSource和EventListener

EventSource 怎么写才不会被忽略

默认情况下,EventSource 发出的事件会被系统丢弃——不是代码没跑,而是没启用监听或没配对提供者名称。关键点在于:类名必须匹配 EventSource 构造时传入的 name,且该 name 会成为 ETW 会话里的 Provider GUID 或字符串标识。

常见错误是直接继承 EventSource 却没指定 name,导致 windows 无法识别这个提供者:

public class MyEventSource : EventSource {     public static MyEventSource Log = new MyEventSource();     [Event(1)] public void RequestStarted(string url) => WriteEvent(1, url); }

这段代码注册的 Provider 名是 MyEventSource(类全名),但实际调试时你会发现 logman query providers | findstr My 找不到它——因为 .NET 默认用 Guid 做 name,而类名只是 fallback。正确做法是显式传入稳定字符串:

  • 使用 [EventSource(Name = "MyCompany.MyApp")] 特性修饰类
  • 确保 name 不含空格、不以数字开头、长度合理(ETW 对 provider name 有字符限制)
  • 避免每次新建实例都 new 一个 EventSource,静态单例是标准实践
  • 发布前务必调用 MyEventSource.Log.Dispose(),否则可能残留未刷新的缓冲事件

EventListener 接收不到事件?检查这三处

EventListener 不是“启动就自动收”,它只监听已启用(enabled)的 Provider。即使你的 EventSource 写对了,没调用 EnableEvents 就等于没打开水龙头。

典型漏点:

  • 忘记在 OnEventSourceCreated 里调用 EnableEvents(eventSource, EventLevel.Informational, EventKeywords.All)
  • 传错 EventLevel:比如 EventSource 里用 [Event(1, Level = EventLevel.Verbose)],但 EnableEvents 只设了 Informational,那这条事件永远进不来
  • Provider name 不一致:EventSource 的 name 是 "MyCompany.MyApp",但你在 EnableEvents 里写成了 "MyApp",匹配失败

验证是否启用成功,可在 OnEventWritten 里加断点,或用 PowerShell 快速确认:

Get-WinEvent -ListProvider "MyCompany.MyApp"

如果返回空,说明 Provider 没注册成功;如果有结果但没事件,大概率是 level 或 keywords 过滤掉了。

性能敏感场景下 EventSource 的坑

EventSource 默认走内核 ETW 缓冲区,看似无锁,但频繁写入仍可能触发缓冲区满、丢事件(EventSource.SendCommand 返回 false)、甚至拖慢线程。尤其在高并发日志场景中,容易误以为“没打出来”是逻辑问题,其实是被限流了。

  • 避免在 [Event] 方法里传大对象(如整个 Exception 实例),应提取关键字段(ex.Message, ex.GetType().Name
  • 不要在 WriteEvent 调用前做耗时计算,例如字符串拼接、json 序列化——这些该提前做好,参数传进去就是最终值
  • 启用 EventSourceSettings.EtwSelfDescribingEventFormat 会增加序列化开销,仅在需要跨平台解析时开启
  • 生产环境建议关闭 Verbose 级别,用 InformationalWarning 控制量级

如何用 dotnet-trace 抓到自己的 EventSource

dotnet-trace 是最轻量的线上诊断工具,但它默认只捕获 SDK 自带 Provider(如 microsoft-Windows-DotNETRuntime)。要抓自定义的,必须显式加 --providers 参数。

命令格式:

dotnet-trace collect --providers "MyCompany.MyApp:0x0000000000000001:5" --duration 30s

其中:

  • MyCompany.MyApp 是你 EventSource.Name
  • 0x0000000000000001 是 keywords 的十六进制掩码(0x1 表示所有 keywords)
  • 5 是 level(5 = Verbose4 = Informational3 = Warning

抓完生成 trace.nettrace,用 dotnet trace convert 转成 JSON 查看,或直接用 PerfView 打开——注意 PerfView 的 “Collect → Run” 页里也要手动勾选你的 Provider 名称,否则照样看不到。

真正难的不是写两行 WriteEvent,而是让事件从进程内存穿过 ETW 内核管道,再被工具稳稳接住。中间任何一环 name、level、keywords、buffer size、enable 时机对不上,都会静默失败。

text=ZqhQzanResources