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

来源:这里教程网 时间:2026-02-21 17:42:25 作者:

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 时机对不上,都会静默失败。

相关推荐