6.9 KiB
Telemetry Events
PowerToys collects limited telemetry to understand feature usage, reliability, and product quality. When adding a new telemetry event, follow the steps below to ensure the event is properly declared, documented, and available after release.
⚠️ Important: Telemetry must never include personal information, file paths, or user‑generated content.
Developer Effort Overview (What to Expect)
Adding a telemetry event is a multi-step process that typically spans several areas of the codebase and documentation.
At a high level, developers should expect to:
- Within one PR:
- Add a new telemetry event(s) to module
- Add the new event(s) DATA_AND_PRIVACY.md
- Reach out to @carlos-zamora or @chatasweetie so internal scripts can process new event(s)
Privacy Guidelines
NEVER log:
- User data (text, files, emails, etc.)
- File paths or filenames
- Personal information
- Sensitive system information
- Anything that could identify a specific user
DO log:
- Feature usage (which features, how often)
- Success/failure status
- Timing/performance metrics
- Error types (not error messages with user data)
- Aggregate counts
Event Naming Convention
Follow this pattern: UtilityName_EventDescription
Examples:
ColorPicker_SessionFancyZones_LayoutAppliedPowerRename_RenameAdvancedPaste_FormatClickedCmdPal_ExtensionInvoked
Adding Telemetry Events to PowerToys
PowerToys uses ETW (Event Tracing for Windows) for telemetry in both C++ and C# modules. The telemetry system is:
- Opt-in by default (disabled since v0.86)
- Privacy-focused - never logs personal info, file paths, or user-generated content
- Controlled by registry - HKEY_CURRENT_USER\Software\Classes\PowerToys\AllowDataDiagnostics
C++ Telemetry Implementation
Core Components
| File | Purpose |
|---|---|
| ProjectTelemetry.h | Declares the global ETW provider g_hProvider |
| TraceBase.h | Base class with RegisterProvider(), UnregisterProvider(), and IsDataDiagnosticsEnabled() check |
| TraceLoggingDefines.h | Privacy tags and telemetry option group macros |
Pattern for C++ Modules
-
Create a
Traceclass inheriting fromtelemetry::TraceBase(src/common/Telemetry/TraceBase.h):// trace.h #pragma once #include <common/Telemetry/TraceBase.h> class Trace : public telemetry::TraceBase { public: static void MyEvent(/* parameters */); }; -
Implement events using
TraceLoggingWriteWrapper:// trace.cpp #include "trace.h" #include <common/Telemetry/TraceBase.h> TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); void Trace::MyEvent(bool enabled) { TraceLoggingWriteWrapper( g_hProvider, "ModuleName_EventName", // Event name TraceLoggingBoolean(enabled, "Enabled"), // Event data ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); }
Key C++ Telemetry Macros
| Macro | Purpose |
|---|---|
TraceLoggingWriteWrapper CustomAction.cpp |
Wraps TraceLoggingWrite with IsDataDiagnosticsEnabled() check |
ProjectTelemetryPrivacyDataTag(tag) TraceLoggingDefines.h |
Sets privacy classification |
C# Telemetry Implementation
Core Components
| File | Purpose |
|---|---|
| PowerToysTelemetry.cs | Singleton Log instance with WriteEvent<T>() method |
| EventBase.cs | Base class for all events (provides EventName, Version) |
| IEvent.cs | Interface requiring PartA_PrivTags property |
| TelemetryBase.cs | Inherits from EventSource, defines ETW constants |
| DataDiagnosticsSettings.cs | Registry-based enable/disable check |
Pattern for C# Modules
-
Create an event class inheriting from
EventBaseand implementingIEvent:using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; namespace MyModule.Telemetry { [EventData] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] public class MyModuleEvent : EventBase, IEvent { // Event properties (logged as telemetry data) public string SomeProperty { get; set; } public int SomeValue { get; set; } // Required: Privacy tag public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; // Optional: Set EventName in constructor (defaults to class name) public MyModuleEvent(string prop, int val) { EventName = "MyModule_EventName"; SomeProperty = prop; SomeValue = val; } } } -
Log the event:
PowerToysTelemetry.Log.WriteEvent(new MyModuleEvent("value", 42));
Privacy Tags (C#)
| Tag | Use Case |
|---|---|
PartA_PrivTags.ProductAndServiceUsage TelemetryBase.cs |
Feature usage events |
PartA_PrivTags.ProductAndServicePerformance TelemetryBase.cs |
Performance/timing events |
Update DATA_AND_PRIVACY.md file
Add your new event(s) to DATA_AND_PRIVACY.md.
Launch Product Version Containing the new events
Events do not become active until they ship in a released PowerToys version. After your PRs are merged:
- The event will begin firing once users install the version that includes it
- In order for PowerToys to process these events, you must complete the next section
Next Steps
Reach out to @carlos-zamora or @chatasweetie so internal scripts can process new event(s).
Summary
Required steps:
- In one PR:
- Add the event(s) in code
- Document event(s) in DATA_AND_PRIVACY.md
- Ship the change in a PowerToys release
- Reach out for next steps