PowerToys 设置界面遥测:Settingsv2 遥测事件体系与 ETW 上报实现解析
【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys
Microsoft PowerToys 的设置界面(内部称 Settingsv2)会针对启动耗时、模块开关等关键行为产生遥测事件。本文基于仓库中的开发文档 telemetry.md 与对应源码,梳理 Settingsv2 遥测事件的继承体系(IEvent/EventBase)、两类核心事件(SettingsBootEvent、SettingsEnabledEvent)的字段与触发时机,以及事件经由 ETW EventSource 上报、由注册表开关控制的完整链路,帮助读者理解 PowerToys 设置界面遥测的设计与实现。
1. Settingsv2 遥测的定位与代码分布
Settingsv2 的遥测事件集中定义在 Settings.UI.Library/Telemetry 目录下。这些事件类都派生自 EventBase 并实现 IEvent 接口,两者位于公共遥测库 ManagedTelemetry 中,被 PowerToys 各模块共享复用:
IEvent提供最底层的抽象,要求每个遥测事件都携带隐私标签(privacy tags)等必备属性;EventBase提供更高层的抽象,承载所有 PowerToys 遥测事件的公共属性。
这种"接口 + 基类"的双层设计把"隐私合规属性"与"通用遥测属性"分离,使得新增一个遥测事件时,开发者只需要关注事件特有的业务字段。
2. 双层抽象的源码剖析
2.1 IEvent:最低层抽象,暴露隐私标签
IEvent.cs 的完整定义只有一个成员:
public interface IEvent { PartA_PrivTags PartA_PrivTags { get; } }PartA_PrivTags是隐私分级标签。在 PowerToys 的遥测体系中,每个事件都必须声明自己的隐私类别,这决定了该事件数据在采集、上报层面的合规处理方式。把它放在接口而非基类中,意味着它是遥测事件的"硬性契约"。
2.2 EventBase:所有 PowerToys 遥测事件的公共属性
EventBase.cs 标记了[EventData]特性(ETW EventSource 用于序列化事件负载),并提供三个公共属性:
| 属性 | 类型 | 说明 |
|---|---|---|
UTCReplace_AppSessionGuid | bool | 恒为true,由遥测基础设施做应用会话 GUID 的 UTC 替换 |
EventName | string | 事件名,上报时作为 ETW 事件名传入 |
Version | string | 懒加载,从当前程序集读取版本号,格式为v{Major}.{Minor}.{Build} |
其中Version属性的实现值得注意:首次访问时才调用GetVersionFromAssembly()从程序集元数据取版本号并缓存,且代码注释明确要求版本格式与 C++ 侧common.cpp中的版本号格式保持一致——从源码结构看,这是为了 .NET 遥测与原生遥测在数据口径上对齐。
3. Settingsv2 生成的两类核心事件
根据 telemetry.md,Settingsv2 生成的事件为SettingsBootEvent与SettingsEnabledEvent。两者都额外标记了[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]特性,配合PowerToysTelemetry.WriteEvent<T>上的泛型约束,保证在 NativeAOT 裁剪场景下事件的公共属性不会被误删。
3.1 SettingsBootEvent:MainWindow 初始化耗时
SettingsBootEvent.cs 捕获 Settingsv2 初始化MainWindowUI 控件所花费的时间:
[EventData] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] public class SettingsBootEvent : EventBase, IEvent { public double BootTimeMs { get; set; } public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance; }字段与隐私标签说明:
BootTimeMs(double,单位毫秒):MainWindow 从构造开始到完成初始化的耗时;PartA_PrivTags:取值为ProductAndServicePerformance,即"性能类"事件,与SettingsEnabledEvent的"使用类"标签形成区分。
上报触发点位于 MainWindow.xaml.cs:构造函数末尾在InitializeComponent()与SetTitleBar()完成、IPC 消息回调注册完毕之后停止秒表并写入事件:
bootTime.Stop(); PowerToysTelemetry.Log.WriteEvent(new SettingsBootEvent() { BootTimeMs = bootTime.ElapsedMilliseconds });可以推断,该事件用于持续观测设置界面 UI 启动性能,例如 XAML 加载、设置加载、模块初始化等步骤引入的回归都能通过BootTimeMs的分布变化被发现。
3.2 SettingsEnabledEvent:模块启用/禁用行为
SettingsEnabledEvent.cs 在用户启用或禁用某个模块时生成:
public class SettingsEnabledEvent : EventBase, IEvent { public string Name { get; set; } // 模块名(实为承载该开关的属性名) public bool Value { get; set; } // true 表示启用,false 表示禁用 public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; }其上报封装在 EnabledModules.cs 的私有静态方法中:
private static void LogTelemetryEvent(bool value, [CallerMemberName] string moduleName = null) { var dataEvent = new SettingsEnabledEvent() { Value = value, Name = moduleName, }; PowerToysTelemetry.Log.WriteEvent(dataEvent); }两个实现细节值得注意:
[CallerMemberName]自动填参:每个模块开关属性的 setter 在值变化时调用LogTelemetryEvent(value)(不带第二个参数),编译器会把调用方成员名(即属性名,如GrabAndMove、PowerDisplay)自动填入Name字段。新增一个模块开关时,开发者无需手写模块名字符串,天然避免拼写漂移;- 仅在值真正变化时上报:setter 中先比较
if (grabAndMove != value)再触发LogTelemetryEvent+NotifyChange(),因此重复设置相同值不会产生冗余事件。
从源码结构看,该事件并非只覆盖模块总开关:PowerPreviewProperties.cs 中也复用了同一个SettingsEnabledEvent类型(同样借助[CallerMemberName]取属性名),用于追踪 Power Preview 具体配置项的启用/禁用,可见这类"配置项开关"遥测是共用同一事件模型的。
4. 上报通道:ETW EventSource 与诊断数据开关
两类事件最终都通过 PowerToysTelemetry 单例写入:
public class PowerToysTelemetry : TelemetryBase { private const string EventSourceName = "Microsoft.PowerToys"; public static PowerToysTelemetry Log { get; } = new PowerToysTelemetry(); public void WriteEvent<T>(T telemetryEvent) where T : EventBase, IEvent { if (DataDiagnosticsSettings.GetEnabledValue()) { this.Write<T>( telemetryEvent.EventName, new EventSourceOptions() { Keywords = ProjectKeywordMeasure, Tags = ProjectTelemetryTagProductAndServicePerformance, }, telemetryEvent); } } }可以确认的机制:
- 传输协议:PowerToys 的托管侧遥测基于 .NET 的
EventSource(ETW),事件源名为Microsoft.PowerToys,WriteEvent<T>的泛型约束where T : EventBase, IEvent与事件类上的[EventData]、[DynamicallyAccessedMembers]特性共同保证事件负载可被 ETW 正确序列化且 AOT 裁剪安全; - 写入前置门禁:每次写入前都会调用
DataDiagnosticsSettings.GetEnabledValue(),未开启诊断数据时事件直接丢弃,不做任何序列化开销; - 事件选项:统一使用
Keywords = ProjectKeywordMeasure与Tags = ProjectTelemetryTagProductAndServicePerformance这两个项目级常量,便于采集端按关键字/标签过滤。
4.1 诊断数据开关的存储位置
DataDiagnosticsSettings.cs 表明遥测开关并非保存在 PowerToys 自己的设置 JSON 中,而是写入注册表,供所有进程统一读取:
| 注册表项 | 说明 |
|---|---|
| 键路径 | HKEY_CURRENT_USER\Software\Classes\PowerToys\ |
AllowDataDiagnostics | 主开关,1允许 /0或不存在则不允许(默认0,即默认关闭) |
DataDiagnosticsUserAction | 记录用户是否主动操作过该开关 |
DataDiagnosticsViewEnabled | 是否允许查看已采集数据 |
所有读取路径都包裹在 try/catch 中且默认返回false——从源码结构看,这意味着即使注册表访问失败(权限等问题),遥测也倾向于"静默不上报",把隐私安全作为默认行为。对使用者而言,这一开关对应 PowerToys 设置界面"常规"页中的诊断数据选项。
5. 事件全集与扩展方式
虽然 telemetry.md 只列出了 Settingsv2 的两类代表性事件,Settings.UI.Library/Telemetry/Events 目录中实际上还定义了更多事件类,例如ModuleLaunchedFromSettingsEvent、TrayFlyoutActivatedEvent、ShortcutConflictDetectedEvent/ShortcutConflictResolvedEvent,以及一组Oobe*Event(OobeStartedEvent、OobeModuleRunEvent、OobeSettingsEvent等,用于首次运行体验引导)和CmdNotFoundInstallEvent/CmdNotFoundUninstallEvent。它们的结构都与上文一致:继承EventBase、实现IEvent、标注[EventData]与[DynamicallyAccessedMembers]。
若要为 Settingsv2 新增一个遥测事件,从现有实现可以归纳出固定套路:
- 在 Settings.UI.Library/Telemetry/Events 下新建事件类,继承
EventBase并实现IEvent,声明事件特有属性与PartA_PrivTags; - 按语义选择隐私标签:性能数据用
ProductAndServicePerformance,行为数据用ProductAndServiceUsage; - 在业务代码的触发点调用
PowerToysTelemetry.Log.WriteEvent(new XxxEvent { ... }),无需关心上报通道与诊断开关——门禁逻辑已内聚在WriteEvent中。
6. 小结与延伸阅读
Settingsv2 的遥测体系可以用一条链路概括:事件类(业务字段 + 隐私标签)→PowerToysTelemetry.WriteEvent(诊断开关门禁)→ ETW EventSource "Microsoft.PowerToys"(注册表AllowDataDiagnostics控制采集)。IEvent/EventBase的双层抽象让隐私契约与通用属性各司其职,而SettingsBootEvent(启动耗时)与SettingsEnabledEvent(模块开关,借助[CallerMemberName]自动记录模块名)分别覆盖了性能与使用行为两条观测维度。
如需进一步理解设置界面整体架构与模块间通信,可参考同目录下的 project-overview.md、settings-implementation.md 与 ui-architecture.md;遥测基础设施的公共文档见 Telemetry/readme.md。
【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考