2021-10-25 10:56:00 +03:00
|
|
|
|
// 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.Globalization;
|
2024-11-11 10:42:40 +01:00
|
|
|
|
using System.IO;
|
2021-10-25 10:56:00 +03:00
|
|
|
|
using System.Reflection;
|
2024-11-26 21:38:41 +01:00
|
|
|
|
using System.Runtime.CompilerServices;
|
2024-09-16 16:09:43 -04:00
|
|
|
|
|
2024-08-08 15:26:43 +01:00
|
|
|
|
using PowerToys.Interop;
|
2021-10-25 10:56:00 +03:00
|
|
|
|
|
2023-03-21 10:27:29 +01:00
|
|
|
|
namespace ManagedCommon
|
2021-10-25 10:56:00 +03:00
|
|
|
|
{
|
|
|
|
|
|
public static class Logger
|
|
|
|
|
|
{
|
|
|
|
|
|
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
2023-03-21 10:27:29 +01:00
|
|
|
|
private static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion;
|
2021-10-25 10:56:00 +03:00
|
|
|
|
|
|
|
|
|
|
private static readonly string Error = "Error";
|
|
|
|
|
|
private static readonly string Warning = "Warning";
|
|
|
|
|
|
private static readonly string Info = "Info";
|
|
|
|
|
|
private static readonly string Debug = "Debug";
|
|
|
|
|
|
private static readonly string TraceFlag = "Trace";
|
|
|
|
|
|
|
2023-03-21 10:27:29 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Initializes the logger and sets the path for logging.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <example>InitializeLogger("\\FancyZones\\Editor\\Logs")</example>
|
|
|
|
|
|
/// <param name="applicationLogPath">The path to the log files folder.</param>
|
|
|
|
|
|
/// <param name="isLocalLow">If the process using Logger is a low-privilege process.</param>
|
|
|
|
|
|
public static void InitializeLogger(string applicationLogPath, bool isLocalLow = false)
|
2021-10-25 10:56:00 +03:00
|
|
|
|
{
|
2023-03-21 10:27:29 +01:00
|
|
|
|
if (isLocalLow)
|
2021-10-25 10:56:00 +03:00
|
|
|
|
{
|
2023-03-21 10:27:29 +01:00
|
|
|
|
applicationLogPath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath + "\\" + Version;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
applicationLogPath = Constants.AppDataPath() + applicationLogPath + "\\" + Version;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-11 10:42:40 +01:00
|
|
|
|
if (!Directory.Exists(applicationLogPath))
|
2023-03-21 10:27:29 +01:00
|
|
|
|
{
|
2024-11-11 10:42:40 +01:00
|
|
|
|
Directory.CreateDirectory(applicationLogPath);
|
2021-10-25 10:56:00 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-11 10:42:40 +01:00
|
|
|
|
var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
|
2021-10-25 10:56:00 +03:00
|
|
|
|
|
|
|
|
|
|
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
|
|
|
|
|
|
|
|
|
|
|
|
Trace.AutoFlush = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
public static void LogError(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log(message, Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
public static void LogError(string message, Exception ex)
|
|
|
|
|
|
{
|
2024-10-18 11:11:18 +02:00
|
|
|
|
if (ex == null)
|
|
|
|
|
|
{
|
2024-11-26 21:38:41 +01:00
|
|
|
|
Log(message, Error);
|
2024-10-18 11:11:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var exMessage =
|
|
|
|
|
|
message + Environment.NewLine +
|
|
|
|
|
|
ex.GetType() + ": " + ex.Message + Environment.NewLine;
|
|
|
|
|
|
|
|
|
|
|
|
if (ex.InnerException != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
exMessage +=
|
|
|
|
|
|
"Inner exception: " + Environment.NewLine +
|
|
|
|
|
|
ex.InnerException.GetType() + ": " + ex.InnerException.Message + Environment.NewLine;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
exMessage +=
|
|
|
|
|
|
"Stack trace: " + Environment.NewLine +
|
|
|
|
|
|
ex.StackTrace;
|
|
|
|
|
|
|
|
|
|
|
|
Log(exMessage, Error);
|
|
|
|
|
|
}
|
2021-10-25 10:56:00 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
public static void LogWarning(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log(message, Warning);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
public static void LogInfo(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log(message, Info);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
public static void LogDebug(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log(message, Debug);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
public static void LogTrace()
|
|
|
|
|
|
{
|
|
|
|
|
|
Log(string.Empty, TraceFlag);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
private static void Log(string message, string type)
|
|
|
|
|
|
{
|
|
|
|
|
|
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo());
|
|
|
|
|
|
Trace.Indent();
|
|
|
|
|
|
if (message != string.Empty)
|
|
|
|
|
|
{
|
|
|
|
|
|
Trace.WriteLine(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Trace.Unindent();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
2021-10-25 10:56:00 +03:00
|
|
|
|
private static string GetCallerInfo()
|
|
|
|
|
|
{
|
2023-03-21 10:27:29 +01:00
|
|
|
|
StackTrace stackTrace = new();
|
2021-10-25 10:56:00 +03:00
|
|
|
|
|
2024-11-26 21:38:41 +01:00
|
|
|
|
var callerMethod = GetCallerMethod(stackTrace);
|
|
|
|
|
|
|
|
|
|
|
|
return $"{callerMethod?.DeclaringType?.Name}::{callerMethod.Name}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static MethodBase GetCallerMethod(StackTrace stackTrace)
|
|
|
|
|
|
{
|
|
|
|
|
|
const int topFrame = 3;
|
|
|
|
|
|
|
|
|
|
|
|
var topMethod = stackTrace.GetFrame(topFrame)?.GetMethod();
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (topMethod?.Name == nameof(IAsyncStateMachine.MoveNext) && typeof(IAsyncStateMachine).IsAssignableFrom(topMethod?.DeclaringType))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Async method; return actual method as determined by heuristic:
|
|
|
|
|
|
// "Nearest method on stack to async state-machine's MoveNext() in same namespace but in a different type".
|
|
|
|
|
|
// There are tighter ways of determining the actual method, but this is good enough and probably faster.
|
|
|
|
|
|
for (int deepFrame = topFrame + 1; deepFrame < stackTrace.FrameCount; deepFrame++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var deepMethod = stackTrace.GetFrame(deepFrame)?.GetMethod();
|
|
|
|
|
|
|
|
|
|
|
|
if (deepMethod?.DeclaringType != topMethod?.DeclaringType && deepMethod?.DeclaringType?.Namespace == topMethod?.DeclaringType?.Namespace)
|
|
|
|
|
|
{
|
|
|
|
|
|
return deepMethod;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Ignore exceptions in Release. The code above won't throw, but if it does, we don't want to crash the app.
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
|
throw;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return topMethod;
|
2021-10-25 10:56:00 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|