This commit is contained in:
Leilei Zhang
2025-12-08 18:33:57 +08:00
parent 1e21254c6a
commit 1a0cc02909
4 changed files with 153 additions and 0 deletions

View File

@@ -142,6 +142,8 @@ internal static class LayoutCommands
public static (int ExitCode, string Output) SetLayout(string[] args, Action<uint> notifyFancyZones, uint wmPrivAppliedLayoutsFileUpdate)
{
Logger.LogInfo($"SetLayout called with args: [{string.Join(", ", args)}]");
if (args.Length == 0)
{
return (1, "Error: set-layout requires a UUID parameter");
@@ -251,9 +253,11 @@ internal static class LayoutCommands
// Write back to file
FancyZonesData.WriteAppliedLayouts(appliedLayouts);
Logger.LogInfo($"Applied layouts file updated for {monitorsToUpdate.Count} monitor(s)");
// Notify FancyZones to reload
notifyFancyZones(wmPrivAppliedLayoutsFileUpdate);
Logger.LogInfo("FancyZones notified of layout change");
string layoutName = targetCustomLayout?.Name ?? targetTemplate?.Type ?? uuid;
if (applyToAll)

View File

@@ -84,9 +84,12 @@ internal static class FancyZonesData
result = null;
error = null;
Logger.LogDebug($"Reading file: {filePath}");
if (!File.Exists(filePath))
{
error = $"File not found: {Path.GetFileName(filePath)}";
Logger.LogWarning(error);
return false;
}
@@ -97,19 +100,23 @@ internal static class FancyZonesData
if (result == null)
{
error = $"Failed to parse {Path.GetFileName(filePath)}";
Logger.LogError(error);
return false;
}
Logger.LogDebug($"Successfully read {Path.GetFileName(filePath)}");
return true;
}
catch (JsonException ex)
{
error = $"JSON parse error in {Path.GetFileName(filePath)}: {ex.Message}";
Logger.LogError(error, ex);
return false;
}
catch (IOException ex)
{
error = $"Failed to read {Path.GetFileName(filePath)}: {ex.Message}";
Logger.LogError(error, ex);
return false;
}
}
@@ -127,7 +134,9 @@ internal static class FancyZonesData
private static void WriteJsonFile<T>(string filePath, T data, JsonTypeInfo<T> jsonTypeInfo)
{
Logger.LogDebug($"Writing file: {filePath}");
var json = JsonSerializer.Serialize(data, jsonTypeInfo);
File.WriteAllText(filePath, json);
Logger.LogInfo($"Successfully wrote {Path.GetFileName(filePath)}");
}
}

View File

@@ -0,0 +1,126 @@
// 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.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
namespace FancyZonesCLI;
/// <summary>
/// Simple logger for FancyZones CLI.
/// Logs to %LOCALAPPDATA%\Microsoft\PowerToys\FancyZones\CLI\Logs
/// </summary>
internal static class Logger
{
private static readonly object LockObj = new();
private static string _logFilePath = string.Empty;
private static bool _isInitialized;
/// <summary>
/// Gets the path to the current log file.
/// </summary>
public static string LogFilePath => _logFilePath;
/// <summary>
/// Initializes the logger.
/// </summary>
public static void InitializeLogger()
{
if (_isInitialized)
{
return;
}
try
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var logDirectory = Path.Combine(localAppData, "Microsoft", "PowerToys", "FancyZones", "CLI", "Logs");
if (!Directory.Exists(logDirectory))
{
Directory.CreateDirectory(logDirectory);
}
var logFileName = $"FancyZonesCLI_{DateTime.Now:yyyy-MM-dd}.log";
_logFilePath = Path.Combine(logDirectory, logFileName);
_isInitialized = true;
LogInfo("FancyZones CLI started");
}
catch
{
// Silently fail if logging cannot be initialized
}
}
/// <summary>
/// Logs an error message.
/// </summary>
public static void LogError(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
Log("ERROR", message, memberName, sourceFilePath, sourceLineNumber);
}
/// <summary>
/// Logs an error message with exception details.
/// </summary>
public static void LogError(string message, Exception ex, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
var fullMessage = ex == null
? message
: $"{message} | Exception: {ex.GetType().Name}: {ex.Message}";
Log("ERROR", fullMessage, memberName, sourceFilePath, sourceLineNumber);
}
/// <summary>
/// Logs a warning message.
/// </summary>
public static void LogWarning(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
Log("WARN", message, memberName, sourceFilePath, sourceLineNumber);
}
/// <summary>
/// Logs an informational message.
/// </summary>
public static void LogInfo(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
Log("INFO", message, memberName, sourceFilePath, sourceLineNumber);
}
/// <summary>
/// Logs a debug message (only in DEBUG builds).
/// </summary>
[System.Diagnostics.Conditional("DEBUG")]
public static void LogDebug(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
{
Log("DEBUG", message, memberName, sourceFilePath, sourceLineNumber);
}
private static void Log(string level, string message, string memberName, string sourceFilePath, int sourceLineNumber)
{
if (!_isInitialized || string.IsNullOrEmpty(_logFilePath))
{
return;
}
try
{
var fileName = Path.GetFileName(sourceFilePath);
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture);
var logEntry = $"[{timestamp}] [{level}] [{fileName}:{sourceLineNumber}] [{memberName}] {message}{Environment.NewLine}";
lock (LockObj)
{
File.AppendAllText(_logFilePath, logEntry);
}
}
catch
{
// Silently fail if logging fails
}
}
}

View File

@@ -13,6 +13,10 @@ internal sealed class Program
{
private static int Main(string[] args)
{
// Initialize logger
Logger.InitializeLogger();
Logger.LogInfo($"CLI invoked with args: [{string.Join(", ", args)}]");
// Initialize Windows messages
NativeMethods.InitializeWindowMessages();
@@ -48,6 +52,16 @@ internal sealed class Program
};
}
// Log result
if (result.ExitCode == 0)
{
Logger.LogInfo($"Command completed successfully");
}
else
{
Logger.LogWarning($"Command failed with exit code {result.ExitCode}: {result.Output}");
}
// Output result
if (!string.IsNullOrEmpty(result.Output))
{