mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 10:16:24 +02:00
171 lines
5.5 KiB
C#
171 lines
5.5 KiB
C#
// 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;
|
|
}
|
|
}
|
|
}
|
|
}
|