Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions DATA_AND_PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ _If you want to find diagnostic data events in the source code, these two links
<tr>
<td>Microsoft.PowerToys.AwakeTimedKeepAwakeEvent</td>
<td>Triggered when the system is kept awake for a specified time duration.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.Awake_CLICommand</td>
<td>Triggered when an Awake CLI command is executed, logging the command name and success status.</td>
</tr>
</table>

Expand Down Expand Up @@ -592,6 +596,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.FileLocksmith_QueryContextMenuError</td>
<td>Occurs when there is an error querying the context menu for File Locksmith.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.FileLocksmith_CLICommand</td>
<td>Triggered when a File Locksmith CLI command is executed, logging the operation mode (query, kill, query-wait, query-json, or help) and success status.</td>
</tr>
</table>

### FileExplorerAddOns
Expand Down Expand Up @@ -776,6 +784,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.ImageResizer_QueryContextMenuError</td>
<td>Triggered when there is an error querying the context menu for Image Resizer.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.ImageResizer_CLICommand</td>
<td>Triggered when an Image Resizer CLI command is executed, logging the command name and success status.</td>
</tr>
</table>

### Keyboard Manager
Expand Down
17 changes: 9 additions & 8 deletions src/modules/FileLocksmith/FileLocksmithCLI/CLILogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
if (argc < 2)
{
Logger::warn("No arguments provided");
return { 1, get_usage(strings) };
return { 1, get_usage(strings), L"help" };
}

bool json_output = false;
Expand Down Expand Up @@ -156,18 +156,18 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
catch (...)
{
Logger::error("Invalid timeout value");
return { 1, strings.GetString(IDS_ERROR_INVALID_TIMEOUT) };
return { 1, strings.GetString(IDS_ERROR_INVALID_TIMEOUT), L"query-wait" };
}
}
else
{
Logger::error("Timeout argument missing");
return { 1, strings.GetString(IDS_ERROR_TIMEOUT_ARG) };
return { 1, strings.GetString(IDS_ERROR_TIMEOUT_ARG), L"query-wait" };
}
}
else if (arg == L"--help")
{
return { 0, get_usage(strings) };
return { 0, get_usage(strings), L"help" };
}
else
{
Expand All @@ -178,7 +178,7 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
if (paths.empty())
{
Logger::error("No paths specified");
return { 1, strings.GetString(IDS_ERROR_NO_PATHS) };
return { 1, strings.GetString(IDS_ERROR_NO_PATHS), L"query" };
}

Logger::info("Processing {} paths", paths.size());
Expand Down Expand Up @@ -213,13 +213,13 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
{
Logger::warn("Timeout waiting for files to be unlocked");
ss << strings.GetString(IDS_TIMEOUT);
return { 1, ss.str() };
return { 1, ss.str(), L"query-wait" };
}
}

Sleep(200);
}
return { 0, ss.str() };
return { 0, ss.str(), L"query-wait" };
}

auto results = finder.find(paths);
Expand All @@ -244,5 +244,6 @@ CommandResult run_command(int argc, wchar_t* argv[], IProcessFinder& finder, IPr
output_ss << get_text(results, strings);
}

return { 0, output_ss.str() };
std::wstring cmd_name = kill ? L"kill" : (json_output ? L"query-json" : L"query");
return { 0, output_ss.str(), cmd_name };
}
1 change: 1 addition & 0 deletions src/modules/FileLocksmith/FileLocksmithCLI/CLILogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct CommandResult
{
int exit_code;
std::wstring output;
std::wstring command_name;
};

struct IProcessFinder
Expand Down
5 changes: 5 additions & 0 deletions src/modules/FileLocksmith/FileLocksmithCLI/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "pch.h"
#include "CLILogic.h"
#include "FileLocksmithLib/FileLocksmith.h"
#include "FileLocksmithLib/Trace.h"
#include <iostream>
#include "resource.h"
#include <common/logger/logger.h>
Expand Down Expand Up @@ -47,6 +48,7 @@ struct RealStringProvider : IStringProvider
int wmain(int argc, wchar_t* argv[])
{
winrt::init_apartment();
Trace::RegisterProvider();
LoggerHelpers::init_logger(L"FileLocksmithCLI", L"", LogSettings::fileLocksmithLoggerName);
Logger::info("FileLocksmithCLI started");

Expand All @@ -65,7 +67,10 @@ int wmain(int argc, wchar_t* argv[])
Logger::info("Command succeeded");
}

Trace::CLICommand(result.command_name.c_str(), result.exit_code == 0);

std::wcout << result.output;
Trace::UnregisterProvider();
return result.exit_code;
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace FileLocksmithCLIUnitTests
auto result = run_command(1, argv, finder, terminator, strings);

Assert::AreEqual(1, result.exit_code);
Assert::AreEqual(std::wstring(L"help"), result.command_name);
}

TEST_METHOD(TestHelp)
Expand All @@ -64,6 +65,7 @@ namespace FileLocksmithCLIUnitTests
auto result = run_command(2, argv, finder, terminator, strings);

Assert::AreEqual(0, result.exit_code);
Assert::AreEqual(std::wstring(L"help"), result.command_name);
}

TEST_METHOD(TestFindProcesses)
Expand All @@ -77,6 +79,7 @@ namespace FileLocksmithCLIUnitTests
auto result = run_command(2, argv, finder, terminator, strings);

Assert::AreEqual(0, result.exit_code);
Assert::AreEqual(std::wstring(L"query"), result.command_name);
Assert::IsTrue(result.output.find(L"123") != std::wstring::npos);
Assert::IsTrue(result.output.find(L"process") != std::wstring::npos);
}
Expand All @@ -94,6 +97,7 @@ namespace FileLocksmithCLIUnitTests
Microsoft::VisualStudio::CppUnitTestFramework::Logger::WriteMessage(result.output.c_str());

Assert::AreEqual(0, result.exit_code);
Assert::AreEqual(std::wstring(L"query-json"), result.command_name);
Assert::IsTrue(result.output.find(L"\"pid\"") != std::wstring::npos);
Assert::IsTrue(result.output.find(L"123") != std::wstring::npos);
}
Expand All @@ -109,6 +113,7 @@ namespace FileLocksmithCLIUnitTests
auto result = run_command(3, argv, finder, terminator, strings);

Assert::AreEqual(0, result.exit_code);
Assert::AreEqual(std::wstring(L"kill"), result.command_name);
Assert::AreEqual((size_t)1, terminator.terminatedPids.size());
Assert::AreEqual((DWORD)123, terminator.terminatedPids[0]);
}
Expand All @@ -125,6 +130,7 @@ namespace FileLocksmithCLIUnitTests
auto result = run_command(5, argv, finder, terminator, strings);

Assert::AreEqual(1, result.exit_code);
Assert::AreEqual(std::wstring(L"query-wait"), result.command_name);
}
};
}
11 changes: 11 additions & 0 deletions src/modules/FileLocksmith/FileLocksmithLib/Trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,14 @@ void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept
TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

void Trace::CLICommand(_In_ PCWSTR commandName, _In_ bool successful) noexcept
{
TraceLoggingWriteWrapper(
g_hProvider,
"FileLocksmith_CLICommand",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingWideString(commandName, "CommandName"),
TraceLoggingBoolean(successful, "Successful"));
}
1 change: 1 addition & 0 deletions src/modules/FileLocksmith/FileLocksmithLib/Trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ class Trace : public telemetry::TraceBase
static void Invoked() noexcept;
static void InvokedRet(_In_ HRESULT hr) noexcept;
static void QueryContextMenuError(_In_ HRESULT hr) noexcept;
static void CLICommand(_In_ PCWSTR commandName, _In_ bool successful) noexcept;
};
24 changes: 23 additions & 1 deletion src/modules/awake/Awake/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Awake.Core.Models;
using Awake.Core.Native;
using Awake.Properties;
using Awake.Telemetry;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
Expand Down Expand Up @@ -68,6 +69,7 @@ private static async Task<int> Main(string[] args)
if (parseResult.Errors.Count > 0)
{
// Shows errors and returns non-zero.
LogCLITelemetry(successful: false);
return rootCommand.Invoke(args);
}

Expand Down Expand Up @@ -97,13 +99,15 @@ private static async Task<int> Main(string[] args)
// Awake is already running - there is no need for us to process
// anything further
Exit(Core.Constants.AppName + " is already running! Exiting the application.", 1);
LogCLITelemetry(successful: false);
return 1;
}
else
{
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAwakeEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
Exit("PowerToys.Awake tried to start with a group policy setting that disables the tool. Please contact your system administrator.", 1);
LogCLITelemetry(successful: false);
return 1;
}
else
Expand All @@ -125,7 +129,9 @@ private static async Task<int> Main(string[] args)
Bridge.GetPwrCapabilities(out _powerCapabilities);
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities, _serializerOptions));

return await rootCommand.InvokeAsync(args);
var result = await rootCommand.InvokeAsync(args);
LogCLITelemetry(successful: result == 0);
return result;
}
}
}
Expand Down Expand Up @@ -216,6 +222,22 @@ private static RootCommand BuildRootCommand()
return rootCommand;
}

private static void LogCLITelemetry(bool successful)
{
try
{
PowerToysTelemetry.Log.WriteEvent(new AwakeCLICommandEvent
{
CommandName = "awake",
Successful = successful,
});
}
catch (Exception ex)
{
Logger.LogError($"Failed to log CLI telemetry: {ex.Message}");
}
}

private static void AwakeUnhandledExceptionCatcher(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception exception)
Expand Down
37 changes: 37 additions & 0 deletions src/modules/awake/Awake/Telemetry/AwakeCLICommandEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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;
using Microsoft.PowerToys.Telemetry.Events;

namespace Awake.Telemetry
{
/// <summary>
/// Telemetry event for Awake CLI command execution.
/// </summary>
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class AwakeCLICommandEvent : EventBase, IEvent
{
public AwakeCLICommandEvent()
{
EventName = "Awake_CLICommand";
CommandName = string.Empty;
}

/// <summary>
/// Gets or sets the name of the CLI command that was executed.
/// </summary>
public string CommandName { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the command executed successfully.
/// </summary>
public bool Successful { get; set; }

public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}
23 changes: 22 additions & 1 deletion src/modules/imageresizer/ImageResizerCLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
using System.Text;

using ImageResizer.Cli;
using ImageResizer.Cli.Telemetry;
using ManagedCommon;
using Microsoft.PowerToys.Telemetry;

namespace ImageResizerCLI;

Expand Down Expand Up @@ -37,14 +39,33 @@ private static int Main(string[] args)
try
{
var executor = new ImageResizerCliExecutor();
return executor.Run(args);
int result = executor.Run(args);
LogCLITelemetry(result == 0);
return result;
}
catch (Exception ex)
{
CliLogger.Error($"Unhandled exception: {ex.Message}");
CliLogger.Error($"Stack trace: {ex.StackTrace}");
Console.Error.WriteLine($"Fatal error: {ex.Message}");
LogCLITelemetry(successful: false);
return 1;
}
}

private static void LogCLITelemetry(bool successful)
{
try
{
PowerToysTelemetry.Log.WriteEvent(new ImageResizerCLICommandEvent
{
CommandName = "resize",
Successful = successful,
});
}
catch (Exception ex)
{
CliLogger.Error($"Failed to log CLI telemetry: {ex.Message}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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;
using Microsoft.PowerToys.Telemetry.Events;

namespace ImageResizer.Cli.Telemetry
{
/// <summary>
/// Telemetry event for Image Resizer CLI command execution.
/// </summary>
[EventData]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
public class ImageResizerCLICommandEvent : EventBase, IEvent
{
public ImageResizerCLICommandEvent()
{
EventName = "ImageResizer_CLICommand";
CommandName = string.Empty;
}

/// <summary>
/// Gets or sets the name of the CLI command that was executed.
/// </summary>
public string CommandName { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the command executed successfully.
/// </summary>
public bool Successful { get; set; }

public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}
Loading