diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index ff20577b..87ecc242 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -7,8 +7,10 @@ using Snap.Hutao.Core; using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Core.LifeCycle.InterProcess; +using Snap.Hutao.Core.Logging; using Snap.Hutao.Core.Shell; using System.Diagnostics; +using static Snap.Hutao.Core.Logging.ConsoleVirtualTerminalSequences; namespace Snap.Hutao; @@ -22,7 +24,7 @@ namespace Snap.Hutao; public sealed partial class App : Application { private const string ConsoleBanner = $""" - {"\u001b[33m"}---------------------------------------------------------------- + {ForegroundYellow}---------------------------------------------------------------- _____ _ _ _ / ____| | | | | | | | (___ _ __ __ _ _ __ | |__| | _ _ | |_ __ _ ___ @@ -34,7 +36,7 @@ public sealed partial class App : Application Snap.Hutao is a open source software developed by DGP Studio. Copyright (C) 2022 - 2024 DGP Studio, All Rights Reserved. - ----------------------------------------------------------------{"\u001b[0m"} + ----------------------------------------------------------------{ForegroundDefault} """; private readonly IServiceProvider serviceProvider; @@ -69,7 +71,7 @@ public sealed partial class App : Application return; } - logger.LogInformation(ConsoleBanner); + logger.LogColorizedInformation((ConsoleBanner, ConsoleColor.Yellow)); LogDiagnosticInformation(); // manually invoke @@ -89,8 +91,8 @@ public sealed partial class App : Application { RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService(); - logger.LogInformation("FamilyName: {name}", runtimeOptions.FamilyName); - logger.LogInformation("Version: {version}", runtimeOptions.Version); - logger.LogInformation("LocalCache: {folder}", runtimeOptions.LocalCache); + logger.LogColorizedInformation(("FamilyName: {Name}", ConsoleColor.Cyan), (runtimeOptions.FamilyName, ConsoleColor.Blue)); + logger.LogInformation("Version: {Version}", runtimeOptions.Version); + logger.LogInformation("LocalCache: {Folder}", runtimeOptions.LocalCache); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/ConsoleVirtualTerminalSequences.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/ConsoleVirtualTerminalSequences.cs new file mode 100644 index 00000000..f2ce047e --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/ConsoleVirtualTerminalSequences.cs @@ -0,0 +1,114 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Core.Logging; + +internal static class ConsoleVirtualTerminalSequences +{ + public const string Default = "\u001b[0m"; + public const string Bold = "\u001b[1m"; + + public const string Underline = "\u001b[4m"; + + public const string Negative = "\u001b[7m"; + + public const string NoBold = "\u001b[22m"; + + public const string NoUnderline = "\u001b[24m"; + + public const string Positive = "\u001b[27m"; + + public const string ForegroundBlack = "\u001b[30m"; + public const string ForegroundRed = "\u001b[31m"; + public const string ForegroundGreen = "\u001b[32m"; + public const string ForegroundYellow = "\u001b[33m"; + public const string ForegroundBlue = "\u001b[34m"; + public const string ForegroundMagenta = "\u001b[35m"; + public const string ForegroundCyan = "\u001b[36m"; + public const string ForegroundWhite = "\u001b[37m"; + public const string ForegroundExtended = "\u001b[38m"; + public const string ForegroundDefault = "\u001b[39m"; + public const string BackgroundBlack = "\u001b[40m"; + public const string BackgroundRed = "\u001b[41m"; + public const string BackgroundGreen = "\u001b[42m"; + public const string BackgroundYellow = "\u001b[43m"; + public const string BackgroundBlue = "\u001b[44m"; + public const string BackgroundMagenta = "\u001b[45m"; + public const string BackgroundCyan = "\u001b[46m"; + public const string BackgroundWhite = "\u001b[47m"; + public const string BackgroundExtended = "\u001b[48m"; + public const string BackgroundDefault = "\u001b[49m"; + + public const string BrightForegroundBlack = "\u001b[90m"; + public const string BrightForegroundRed = "\u001b[91m"; + public const string BrightForegroundGreen = "\u001b[92m"; + public const string BrightForegroundYellow = "\u001b[93m"; + public const string BrightForegroundBlue = "\u001b[94m"; + public const string BrightForegroundMagenta = "\u001b[95m"; + public const string BrightForegroundCyan = "\u001b[96m"; + public const string BrightForegroundWhite = "\u001b[97m"; + public const string BrightBackgroundBlack = "\u001b[100m"; + public const string BrightBackgroundRed = "\u001b[101m"; + public const string BrightBackgroundGreen = "\u001b[102m"; + public const string BrightBackgroundYellow = "\u001b[103m"; + public const string BrightBackgroundBlue = "\u001b[104m"; + public const string BrightBackgroundMagenta = "\u001b[105m"; + public const string BrightBackgroundCyan = "\u001b[106m"; + public const string BrightBackgroundWhite = "\u001b[107m"; + + public const string Dim = "\u001b[2m"; + public const string Italic = "\u001b[3m"; + + public const string Blink = "\u001b[5m"; + + public const string Hidden = "\u001b[8m"; + public const string StrikeThrough = "\u001b[9m"; + public const string DoubleUnderline = "\u001b[21m"; + + public const string NoItalic = "\u001b[23m"; + + public const string NoBlink = "\u001b[25m"; + + public const string NoHidden = "\u001b[28m"; + public const string NoStrikeThrough = "\u001b[29m"; + + public static string FromConsoleColor(ConsoleColor color, bool foreground) + { + return (foreground, color) switch + { + (true, ConsoleColor.Black) => ForegroundBlack, + (true, ConsoleColor.DarkBlue) => ForegroundBlue, + (true, ConsoleColor.DarkGreen) => ForegroundGreen, + (true, ConsoleColor.DarkCyan) => ForegroundCyan, + (true, ConsoleColor.DarkRed) => ForegroundRed, + (true, ConsoleColor.DarkMagenta) => ForegroundMagenta, + (true, ConsoleColor.DarkYellow) => ForegroundYellow, + (true, ConsoleColor.DarkGray) => BrightForegroundBlack, + (true, ConsoleColor.Gray) => ForegroundWhite, + (true, ConsoleColor.Blue) => BrightForegroundBlue, + (true, ConsoleColor.Green) => BrightForegroundGreen, + (true, ConsoleColor.Cyan) => BrightForegroundCyan, + (true, ConsoleColor.Red) => BrightForegroundRed, + (true, ConsoleColor.Magenta) => BrightForegroundMagenta, + (true, ConsoleColor.Yellow) => BrightForegroundYellow, + (true, ConsoleColor.White) => BrightForegroundWhite, + (false, ConsoleColor.Black) => BackgroundBlack, + (false, ConsoleColor.DarkBlue) => BackgroundBlue, + (false, ConsoleColor.DarkGreen) => BackgroundGreen, + (false, ConsoleColor.DarkCyan) => BackgroundCyan, + (false, ConsoleColor.DarkRed) => BackgroundRed, + (false, ConsoleColor.DarkMagenta) => BackgroundMagenta, + (false, ConsoleColor.DarkYellow) => BackgroundYellow, + (false, ConsoleColor.DarkGray) => BrightBackgroundBlack, + (false, ConsoleColor.Gray) => BackgroundWhite, + (false, ConsoleColor.Blue) => BrightBackgroundBlue, + (false, ConsoleColor.Green) => BrightBackgroundGreen, + (false, ConsoleColor.Cyan) => BrightBackgroundCyan, + (false, ConsoleColor.Red) => BrightBackgroundRed, + (false, ConsoleColor.Magenta) => BrightBackgroundMagenta, + (false, ConsoleColor.Yellow) => BrightBackgroundYellow, + (false, ConsoleColor.White) => BrightBackgroundWhite, + _ => string.Empty, + }; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/ConsoleWindowLifeTime.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/ConsoleWindowLifeTime.cs index a9fa4b12..09c626d2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/ConsoleWindowLifeTime.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/ConsoleWindowLifeTime.cs @@ -38,4 +38,4 @@ internal sealed class ConsoleWindowLifeTime : IDisposable FreeConsole(); } } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogArgument.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogArgument.cs new file mode 100644 index 00000000..400cee79 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogArgument.cs @@ -0,0 +1,28 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Core.Logging; + +internal readonly struct LogArgument +{ + public readonly object? Argument; + public readonly ConsoleColor? ForegroundColor; + public readonly ConsoleColor? BackgroundColor; + + public LogArgument(object? argument, ConsoleColor? foreground = default, ConsoleColor? background = default) + { + Argument = argument; + ForegroundColor = foreground; + BackgroundColor = background; + } + + public static implicit operator LogArgument(string argument) + { + return new(argument); + } + + public static implicit operator LogArgument((string Argument, ConsoleColor Foreground) tuple) + { + return new(tuple.Argument, tuple.Foreground); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogMessage.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogMessage.cs new file mode 100644 index 00000000..2be179ce --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LogMessage.cs @@ -0,0 +1,28 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Core.Logging; + +internal readonly struct LogMessage +{ + public readonly string Message; + public readonly ConsoleColor? ForegroundColor; + public readonly ConsoleColor? BackgroundColor; + + public LogMessage(string message, ConsoleColor? foreground = default, ConsoleColor? background = default) + { + Message = message; + ForegroundColor = foreground; + BackgroundColor = background; + } + + public static implicit operator LogMessage(string value) + { + return new(value); + } + + public static implicit operator LogMessage((string Value, ConsoleColor Foreground) tuple) + { + return new(tuple.Value, tuple.Foreground); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerExtension.cs new file mode 100644 index 00000000..132cec98 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerExtension.cs @@ -0,0 +1,232 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text; + +namespace Snap.Hutao.Core.Logging; + +[SuppressMessage("", "SH002")] +internal static class LoggerExtension +{ + public static void LogColorizedDebug(this ILogger logger, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Debug, eventId, exception, message, args); + } + + public static void LogColorizedDebug(this ILogger logger, EventId eventId, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Debug, eventId, message, args); + } + + public static void LogColorizedDebug(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Debug, exception, message, args); + } + + public static void LogColorizedDebug(this ILogger logger, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Debug, message, args); + } + + public static void LogColorizedTrace(this ILogger logger, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Trace, eventId, exception, message, args); + } + + public static void LogColorizedTrace(this ILogger logger, EventId eventId, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Trace, eventId, message, args); + } + + public static void LogColorizedTrace(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Trace, exception, message, args); + } + + public static void LogColorizedTrace(this ILogger logger, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Trace, message, args); + } + + public static void LogColorizedInformation(this ILogger logger, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Information, eventId, exception, message, args); + } + + public static void LogColorizedInformation(this ILogger logger, EventId eventId, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Information, eventId, message, args); + } + + public static void LogColorizedInformation(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Information, exception, message, args); + } + + public static void LogColorizedInformation(this ILogger logger, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Information, message, args); + } + + public static void LogColorizedWarning(this ILogger logger, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Warning, eventId, exception, message, args); + } + + public static void LogColorizedWarning(this ILogger logger, EventId eventId, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Warning, eventId, message, args); + } + + public static void LogColorizedWarning(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Warning, exception, message, args); + } + + public static void LogColorizedWarning(this ILogger logger, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Warning, message, args); + } + + public static void LogColorizedError(this ILogger logger, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Error, eventId, exception, message, args); + } + + public static void LogColorizedError(this ILogger logger, EventId eventId, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Error, eventId, message, args); + } + + public static void LogColorizedError(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Error, exception, message, args); + } + + public static void LogColorizedError(this ILogger logger, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Error, message, args); + } + + public static void LogColorizedCritical(this ILogger logger, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Critical, eventId, exception, message, args); + } + + public static void LogColorizedCritical(this ILogger logger, EventId eventId, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Critical, eventId, message, args); + } + + public static void LogColorizedCritical(this ILogger logger, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Critical, exception, message, args); + } + + public static void LogColorizedCritical(this ILogger logger, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(LogLevel.Critical, message, args); + } + + public static void LogColorized(this ILogger logger, LogLevel logLevel, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(logLevel, 0, null, message, args); + } + + public static void LogColorized(this ILogger logger, LogLevel logLevel, EventId eventId, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(logLevel, eventId, null, message, args); + } + + public static void LogColorized(this ILogger logger, LogLevel logLevel, Exception? exception, LogMessage message, params LogArgument[] args) + { + logger.LogColorized(logLevel, 0, exception, message, args); + } + + public static void LogColorized(this ILogger logger, LogLevel logLevel, EventId eventId, Exception? exception, LogMessage message, params LogArgument[] args) + { + string colorizedMessage = Colorize(message, args, out object?[] outArgs)!; + logger.Log(logLevel, eventId, exception, colorizedMessage, outArgs); + } + + private static string? Colorize(LogMessage message, LogArgument[] args, out object?[] outArgs) + { + StringBuilder resultMessageBuilder = new(message.Message.Length); + ReadOnlySpan messageSpan = message.Message.AsSpan(); + + // Message base colors + ConsoleColor? messageForeground = message.ForegroundColor; + ConsoleColor? messageBackground = message.BackgroundColor; + + if (messageForeground.HasValue) + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageForeground.Value, true)); + } + + if (messageBackground.HasValue) + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageBackground.Value, false)); + } + + ReadOnlySpan argSpan = args.AsSpan(); + outArgs = new object?[args.Length]; + + int argIndex = 0; + for (int index = 0; index < messageSpan.Length; index++) + { + if (messageSpan[index] == '{') + { + ref readonly LogArgument arg = ref argSpan[argIndex]; + outArgs[argIndex] = arg.Argument; + argIndex++; + if (arg.ForegroundColor.HasValue) + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(arg.ForegroundColor.Value, true)); + } + + if (arg.BackgroundColor.HasValue) + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(arg.BackgroundColor.Value, false)); + } + + int closingIndex = messageSpan[index..].IndexOf('}'); + resultMessageBuilder.Append(messageSpan.Slice(index, closingIndex + 1)); + + index += closingIndex; + + if (arg.ForegroundColor.HasValue || arg.BackgroundColor.HasValue) + { + // Restore message colors + if (messageForeground.HasValue || messageBackground.HasValue) + { + if (messageForeground.HasValue) + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageForeground.Value, true)); + } + + if (messageBackground.HasValue) + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.FromConsoleColor(messageBackground.Value, false)); + } + } + else + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.Default); + } + } + } + else + { + resultMessageBuilder.Append(messageSpan[index]); + } + } + + // Restore default colors + if (message.ForegroundColor.HasValue || message.BackgroundColor.HasValue) + { + resultMessageBuilder.Append(ConsoleVirtualTerminalSequences.Default); + } + + return resultMessageBuilder.ToString(); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerFactoryExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerFactoryExtension.cs similarity index 88% rename from src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerFactoryExtensions.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerFactoryExtension.cs index 1dd99ff7..fe77f4b7 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerFactoryExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/LoggerFactoryExtension.cs @@ -3,7 +3,7 @@ namespace Snap.Hutao.Core.Logging; -internal static class LoggerFactoryExtensions +internal static class LoggerFactoryExtension { public static ILoggingBuilder AddConsoleWindow(this ILoggingBuilder builder) {