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级别,用
Informational或
Warning控制量级
如何用 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 = Verbose,
4 = Informational,
3 = Warning)
抓完生成
trace.nettrace,用
dotnet trace convert转成 JSON 查看,或直接用 PerfView 打开——注意 PerfView 的 “Collect → Run” 页里也要手动勾选你的 Provider 名称,否则照样看不到。
真正难的不是写两行
WriteEvent,而是让事件从进程内存穿过 ETW 内核管道,再被工具稳稳接住。中间任何一环 name、level、keywords、buffer size、enable 时机对不上,都会静默失败。
