chore: import upstream snapshot with attribution
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
Publish Dev Docs Website / build (push) Failing after 1s
Publish Dev Docs Website / deploy (push) Has been skipped
Spell checking / Report (Push) (push) Has been cancelled
Spell checking / Report (PR) (push) Has been cancelled
Spell checking / Check Spelling (push) Has been cancelled
Spell checking / Update PR (push) Has been cancelled
Publish Dev Docs Website / build (push) Failing after 1s
Publish Dev Docs Website / deploy (push) Has been skipped
Spell checking / Report (Push) (push) Has been cancelled
Spell checking / Report (PR) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Microsoft.PowerToys.Telemetry
|
||||
{
|
||||
public static class DataDiagnosticsSettings
|
||||
{
|
||||
private static readonly string DataDiagnosticsRegistryKey = @"HKEY_CURRENT_USER\Software\Classes\PowerToys\";
|
||||
private static readonly string DataDiagnosticsRegistryValueName = @"AllowDataDiagnostics";
|
||||
private static readonly string DataDiagnosticsDataDiagnosticsUserActionRegistryValueName = @"DataDiagnosticsUserAction";
|
||||
private static readonly string DataDiagnosticsDataDiagnosticsViewDataRegistryValueName = @"DataDiagnosticsViewEnabled";
|
||||
|
||||
public static bool GetEnabledValue()
|
||||
{
|
||||
object registryValue = null;
|
||||
try
|
||||
{
|
||||
registryValue = Registry.GetValue(DataDiagnosticsRegistryKey, DataDiagnosticsRegistryValueName, 0);
|
||||
|
||||
if (registryValue is not null)
|
||||
{
|
||||
return (int)registryValue == 1 ? true : false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetEnabledValue(bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
Registry.SetValue(DataDiagnosticsRegistryKey, DataDiagnosticsRegistryValueName, value ? 1 : 0);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetUserActionValue()
|
||||
{
|
||||
object registryValue = null;
|
||||
try
|
||||
{
|
||||
registryValue = Registry.GetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsUserActionRegistryValueName, 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (registryValue is not null)
|
||||
{
|
||||
return (int)registryValue == 1 ? true : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetUserActionValue(bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
Registry.SetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsUserActionRegistryValueName, value ? 1 : 0);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetViewEnabledValue()
|
||||
{
|
||||
object registryValue = null;
|
||||
try
|
||||
{
|
||||
registryValue = Registry.GetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsViewDataRegistryValueName, 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (registryValue is not null)
|
||||
{
|
||||
return (int)registryValue == 1 ? true : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetViewEnabledValue(bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
Registry.SetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsViewDataRegistryValueName, value ? 1 : 0);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.Tracing.Session;
|
||||
|
||||
namespace Microsoft.PowerToys.Telemetry
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is based loosely on the C++ ETWTrace class in Win32client/Framework project.
|
||||
/// It is intended to record telemetry events generated by the PowerToys processes so that end users
|
||||
/// can view them if they want.
|
||||
/// </summary>
|
||||
public class ETWTrace : IDisposable
|
||||
{
|
||||
internal const EventKeywords TelemetryKeyword = (EventKeywords)0x0000200000000000;
|
||||
internal const EventKeywords MeasuresKeyword = (EventKeywords)0x0000400000000000;
|
||||
internal const EventKeywords CriticalDataKeyword = (EventKeywords)0x0000800000000000;
|
||||
|
||||
private readonly bool telemetryEnabled = DataDiagnosticsSettings.GetEnabledValue(); // This is the global telemetry setting on whether to log events
|
||||
private readonly bool telemetryRecordingEnabled = DataDiagnosticsSettings.GetViewEnabledValue(); // This is the setting for recording telemetry events to disk for viewing
|
||||
private string etwFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\", "etw");
|
||||
private bool disposedValue;
|
||||
private string sessionName;
|
||||
private string etwFilePath;
|
||||
private bool started;
|
||||
#nullable enable
|
||||
private TraceEventSession? traceSession;
|
||||
|
||||
internal sealed class Lister : EventListener
|
||||
{
|
||||
public Lister()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private Lister? listener;
|
||||
#nullable disable
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ETWTrace"/> class.
|
||||
/// </summary>
|
||||
public ETWTrace()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
public ETWTrace(string etwPath)
|
||||
{
|
||||
this.etwFolderPath = etwPath;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (File.Exists(etwFolderPath))
|
||||
{
|
||||
File.Delete(etwFolderPath);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(etwFolderPath))
|
||||
{
|
||||
Directory.CreateDirectory(etwFolderPath);
|
||||
}
|
||||
|
||||
if (this.telemetryEnabled && this.telemetryRecordingEnabled)
|
||||
{
|
||||
this.Start();
|
||||
}
|
||||
|
||||
listener = new Lister();
|
||||
listener.EnableEvents(PowerToysTelemetry.Log, EventLevel.LogAlways);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
this.Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the trace session.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (this.started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
new Task(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(30 * 1000);
|
||||
|
||||
this.traceSession.Flush();
|
||||
}
|
||||
}).Start();
|
||||
|
||||
string executable = Process.GetCurrentProcess().ProcessName;
|
||||
string dateTimeNow = DateTime.Now.ToString("MM-d-yyyy__H_mm_ss", CultureInfo.InvariantCulture);
|
||||
this.sessionName = string.Format(CultureInfo.InvariantCulture, "{0}-{1}-{2}", executable, Environment.ProcessId, dateTimeNow);
|
||||
this.etwFilePath = Path.Combine(etwFolderPath, $"{this.sessionName}.etl");
|
||||
|
||||
this.traceSession = new TraceEventSession(
|
||||
this.sessionName, this.etwFilePath, (TraceEventSessionOptions)(TraceEventSessionOptions.Create | TraceEventSessionOptions.PrivateLogger | TraceEventSessionOptions.PrivateInProcLogger));
|
||||
TraceEventProviderOptions args = new TraceEventProviderOptions();
|
||||
|
||||
this.traceSession.EnableProvider(
|
||||
PowerToysTelemetry.Log.Guid,
|
||||
matchAnyKeywords: (ulong)TelemetryKeyword | (ulong)MeasuresKeyword | (ulong)CriticalDataKeyword);
|
||||
|
||||
this.started = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the trace session.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!this.started)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.traceSession != null)
|
||||
{
|
||||
Trace.TraceInformation("Disposing EventTraceSession");
|
||||
this.traceSession.Dispose();
|
||||
this.traceSession = null;
|
||||
this.started = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the object.
|
||||
/// </summary>
|
||||
/// <param name="disposing">boolean for disposing.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
this.Stop();
|
||||
}
|
||||
|
||||
this.disposedValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
|
||||
namespace Microsoft.PowerToys.Telemetry.Events
|
||||
{
|
||||
[EventData]
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public class DebugEvent : EventBase, IEvent
|
||||
{
|
||||
public string Message { get; set; }
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.PowerToys.Telemetry.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class to implement properties that are common to all telemetry events.
|
||||
/// </summary>
|
||||
[EventData]
|
||||
public class EventBase
|
||||
{
|
||||
public bool UTCReplace_AppSessionGuid => true;
|
||||
|
||||
public string EventName { get; set; }
|
||||
|
||||
private string _version;
|
||||
|
||||
public string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_version))
|
||||
{
|
||||
_version = GetVersionFromAssembly();
|
||||
}
|
||||
|
||||
return _version;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetVersionFromAssembly()
|
||||
{
|
||||
// For consistency this should be formatted the same way as
|
||||
// https://github.com/microsoft/PowerToys/blob/710f92d99965109fd788d85ebf8b6b9e0ba1524a/src/common/common.cpp#L635
|
||||
var version = Assembly.GetExecutingAssembly()?.GetName()?.Version ?? new Version();
|
||||
return $"v{version.Major}.{version.Minor}.{version.Build}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.PowerToys.Telemetry.Events
|
||||
{
|
||||
public interface IEvent
|
||||
{
|
||||
PartA_PrivTags PartA_PrivTags { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>PowerToys Telemetry</Description>
|
||||
<AssemblyName>PowerToys.ManagedTelemetry</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\Telemetry\TelemetryBase.cs" Link="TelemetryBase.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace Microsoft.PowerToys.Telemetry
|
||||
{
|
||||
/// <summary>
|
||||
/// Telemetry helper class for PowerToys.
|
||||
/// </summary>
|
||||
public class PowerToysTelemetry : TelemetryBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Name for ETW event.
|
||||
/// </summary>
|
||||
private const string EventSourceName = "Microsoft.PowerToys";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PowerToysTelemetry"/> class.
|
||||
/// </summary>
|
||||
public PowerToysTelemetry()
|
||||
: base(EventSourceName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of the <see cref="PowerLauncherTelemetry"/> class.
|
||||
/// </summary>
|
||||
public static PowerToysTelemetry Log { get; } = new PowerToysTelemetry();
|
||||
|
||||
/// <summary>
|
||||
/// Publishes ETW event when an action is triggered on
|
||||
/// </summary>
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", Justification = "We will ensure the public properties won't be trimmed by ourself.")]
|
||||
public void WriteEvent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(T telemetryEvent)
|
||||
where T : EventBase, IEvent
|
||||
{
|
||||
if (DataDiagnosticsSettings.GetEnabledValue())
|
||||
{
|
||||
this.Write<T>(
|
||||
telemetryEvent.EventName,
|
||||
new EventSourceOptions()
|
||||
{
|
||||
Keywords = ProjectKeywordMeasure,
|
||||
Tags = ProjectTelemetryTagProductAndServicePerformance,
|
||||
},
|
||||
telemetryEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user