From f5b295f038cc8e60e7dd04e5840d3985add13f4e Mon Sep 17 00:00:00 2001 From: Rod Christiansen Date: Tue, 1 Sep 2026 14:18:54 -0700 Subject: [PATCH] Write log lines in the shared session log format and fix the -v flag Log-file lines are now "[yyyy-MM-dd HH:mm:ss] LEVEL message" in local time, with the level left-aligned in a five-character column and limited to DEBUG, INFO, WARN and ERROR. Section, progress, success and skipped markers are written as INFO lines that carry the marker in the message; the console output keeps its icons and is unchanged. The 30-day retention sweep at logger initialisation is already in place and is unchanged; the README now documents it alongside the line format. "-v" was matched as the version switch before it could be read as verbose, so "managedbootstrapinstall.exe -v" printed the version and exited. "-v" and "--verbose" now both mean verbose; "--version" and "-V" print the version. --- Logger.cs | 84 +++++++++++++++++++++++++++++++++--------------------- Program.cs | 13 +++++---- README.md | 8 ++++-- 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/Logger.cs b/Logger.cs index d0e8b0f..ecefe68 100644 --- a/Logger.cs +++ b/Logger.cs @@ -45,21 +45,21 @@ public static void Initialize(string logDirectory, string version = "Unknown", b PruneExpiredLogs(logDirectory); // Write session header to log file - WriteToFile("=== BootstrapMate Session Started ==="); - WriteToFile($"Version: {version}"); - WriteToFile($"Session Start Time: {_sessionStartTime:yyyy-MM-dd HH:mm:ss.fff}"); - WriteToFile($"Process ID: {Environment.ProcessId}"); - WriteToFile($"User: {Environment.UserName}"); - WriteToFile($"Machine: {Environment.MachineName}"); - WriteToFile($"OS: {Environment.OSVersion}"); - WriteToFile($"Process Architecture: {System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture}"); - WriteToFile($"OS Architecture: {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture}"); - WriteToFile($"Working Directory: {Environment.CurrentDirectory}"); - WriteToFile($"Command Line: {Environment.CommandLine}"); - WriteToFile($"Is Interactive: {Environment.UserInteractive}"); - WriteToFile($"Current User: {System.Security.Principal.WindowsIdentity.GetCurrent().Name}"); - WriteToFile($"Verbose Console: {verboseConsole}"); - WriteToFile($"Silent Mode: {silentMode}"); + WriteToFile(LogLevel.Info, "=== BootstrapMate Session Started ==="); + WriteToFile(LogLevel.Info, $"Version: {version}"); + WriteToFile(LogLevel.Info, $"Session Start Time: {_sessionStartTime:yyyy-MM-dd HH:mm:ss.fff}"); + WriteToFile(LogLevel.Info, $"Process ID: {Environment.ProcessId}"); + WriteToFile(LogLevel.Info, $"User: {Environment.UserName}"); + WriteToFile(LogLevel.Info, $"Machine: {Environment.MachineName}"); + WriteToFile(LogLevel.Info, $"OS: {Environment.OSVersion}"); + WriteToFile(LogLevel.Info, $"Process Architecture: {System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture}"); + WriteToFile(LogLevel.Info, $"OS Architecture: {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture}"); + WriteToFile(LogLevel.Info, $"Working Directory: {Environment.CurrentDirectory}"); + WriteToFile(LogLevel.Info, $"Command Line: {Environment.CommandLine}"); + WriteToFile(LogLevel.Info, $"Is Interactive: {Environment.UserInteractive}"); + WriteToFile(LogLevel.Info, $"Current User: {System.Security.Principal.WindowsIdentity.GetCurrent().Name}"); + WriteToFile(LogLevel.Info, $"Verbose Console: {verboseConsole}"); + WriteToFile(LogLevel.Info, $"Silent Mode: {silentMode}"); } catch (Exception ex) { @@ -144,7 +144,7 @@ public static void Success(string message) private static void Log(LogLevel level, string message) { // Always write to log file with full detail - WriteToFile($"[{level}] {message}"); + WriteToFile(level, message); // Write to console based on level and verbose setting WriteToConsole(level, message); @@ -153,15 +153,35 @@ private static void Log(LogLevel level, string message) WriteToPipe(level, message); } - private static void WriteToFile(string message) + /// + /// Formats one log-file line: [yyyy-MM-dd HH:mm:ss] LEVEL message in local + /// time, with the level left-aligned in a five-character column. The level + /// vocabulary in the file is DEBUG, INFO, WARN and ERROR only; every other + /// classification is written as INFO with any marker carried in the message. + /// + internal static string FormatLine(LogLevel level, string message, DateTime timestamp) + { + return $"[{timestamp:yyyy-MM-dd HH:mm:ss}] {FileLevel(level),-5} {message}"; + } + + private static string FileLevel(LogLevel level) + { + return level switch + { + LogLevel.Debug => "DEBUG", + LogLevel.Warning => "WARN", + LogLevel.Error => "ERROR", + _ => "INFO" + }; + } + + private static void WriteToFile(LogLevel level, string message) { if (string.IsNullOrEmpty(LogFile)) return; try { - string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); - string logEntry = $"[{timestamp}] {message}"; - File.AppendAllText(LogFile, logEntry + Environment.NewLine); + File.AppendAllText(LogFile, FormatLine(level, message, DateTime.Now) + Environment.NewLine); } catch { @@ -230,7 +250,7 @@ private static (string icon, ConsoleColor? color) GetDisplayFormat(LogLevel leve public static void WriteHeader(string title) { var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); - WriteToFile($"=== {title} === (Started: {timestamp})"); + WriteToFile(LogLevel.Info, $"=== {title} === (Started: {timestamp})"); if (_silentMode) return; Console.WriteLine(); Console.WriteLine($"══ {title} ══"); @@ -239,7 +259,7 @@ public static void WriteHeader(string title) public static void WriteSection(string section) { - WriteToFile($"[SECTION] {section}"); + WriteToFile(LogLevel.Info, $"[SECTION] {section}"); if (_silentMode) return; Console.WriteLine(); Console.WriteLine($"[>] {section}"); @@ -247,7 +267,7 @@ public static void WriteSection(string section) public static void WriteProgress(string operation, string item) { - WriteToFile($"[PROGRESS] {operation}: {item}"); + WriteToFile(LogLevel.Info, $"[PROGRESS] {operation}: {item}"); if (_silentMode) return; Console.WriteLine($" [*] {operation}: {item}"); } @@ -255,35 +275,35 @@ public static void WriteProgress(string operation, string item) public static void WriteSubProgress(string status, string details = "") { var message = string.IsNullOrEmpty(details) ? status : $"{status}: {details}"; - WriteToFile($"[SUB-PROGRESS] {message}"); + WriteToFile(LogLevel.Info, $"[SUB-PROGRESS] {message}"); if (_silentMode) return; Console.WriteLine($" • {message}"); } public static void WriteSuccess(string message) { - WriteToFile($"[SUCCESS] {message}"); + WriteToFile(LogLevel.Info, $"[SUCCESS] {message}"); if (_silentMode) return; Console.WriteLine($" [+] {message}"); } public static void WriteWarning(string message) { - WriteToFile($"[WARNING] {message}"); + WriteToFile(LogLevel.Warning, message); if (_silentMode) return; Console.WriteLine($" [!] {message}"); } public static void WriteError(string message) { - WriteToFile($"[ERROR] {message}"); + WriteToFile(LogLevel.Error, message); if (_silentMode) return; Console.WriteLine($" [X] {message}"); } public static void WriteSkipped(string message) { - WriteToFile($"[SKIPPED] {message}"); + WriteToFile(LogLevel.Info, $"[SKIPPED] {message}"); if (_silentMode) return; Console.WriteLine($" [-] {message}"); } @@ -292,7 +312,7 @@ public static void WriteCompletion(string message) { var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var duration = DateTime.Now - _sessionStartTime; - WriteToFile($"[COMPLETION] {message} (Completed: {timestamp}, Total Duration: {duration.TotalSeconds:F1}s)"); + WriteToFile(LogLevel.Info, $"[COMPLETION] {message} (Completed: {timestamp}, Total Duration: {duration.TotalSeconds:F1}s)"); if (_silentMode) return; Console.WriteLine(); Console.WriteLine($"[+] {message}"); @@ -340,9 +360,9 @@ public static void WriteSessionSummary() { var duration = GetSessionDuration(); var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); - WriteToFile($"=== BootstrapMate Session Ended === (Duration: {duration.TotalSeconds:F1}s)"); - WriteToFile($"Session End Time: {timestamp}"); - WriteToFile($"Total Session Duration: {duration.TotalMinutes:F2} minutes"); + WriteToFile(LogLevel.Info, $"=== BootstrapMate Session Ended === (Duration: {duration.TotalSeconds:F1}s)"); + WriteToFile(LogLevel.Info, $"Session End Time: {timestamp}"); + WriteToFile(LogLevel.Info, $"Total Session Duration: {duration.TotalMinutes:F2} minutes"); } } } diff --git a/Program.cs b/Program.cs index 08f6bda..f9c77fe 100644 --- a/Program.cs +++ b/Program.cs @@ -208,9 +208,10 @@ static void WriteLog(string message) static int Main(string[] args) { - // Handle version request immediately without admin check or verbose logging + // Handle version request immediately without admin check or verbose logging. + // Only --version and -V mean version; lowercase -v is the verbose switch. if (args.Length > 0 && (args[0].Equals("--version", StringComparison.OrdinalIgnoreCase) || - args[0].Equals("-v", StringComparison.OrdinalIgnoreCase))) + args[0].Equals("-V", StringComparison.Ordinal))) { Console.WriteLine(Version); return 0; @@ -221,7 +222,7 @@ static int Main(string[] args) // Check for verbose mode bool verboseMode = args.Any(arg => arg.Equals("--verbose", StringComparison.OrdinalIgnoreCase) || - arg.Equals("-v", StringComparison.OrdinalIgnoreCase)); + arg.Equals("-v", StringComparison.Ordinal)); Logger.Initialize(LogDirectory, Version, verboseMode, silentMode); Logger.Debug("Main() called with arguments: " + string.Join(" ", args)); @@ -421,7 +422,7 @@ static async Task MainAsync(string[] args) Console.WriteLine("Options:"); Console.WriteLine(" --url URL to the bootstrapmate.json manifest"); Console.WriteLine(" --force (Deprecated - downloads are always fresh. Cache is for inspection only)"); - Console.WriteLine(" --verbose Show detailed logging output"); + Console.WriteLine(" --verbose, -v Show detailed logging output"); Console.WriteLine(" --silent Run completely silently (no console output)"); Console.WriteLine(" --no-dialog Disable progress dialog (csharpdialog)"); Console.WriteLine(" --dialog-title Custom title for progress dialog"); @@ -430,7 +431,7 @@ static async Task MainAsync(string[] args) Console.WriteLine(" --save-settings Save GUI settings to registry"); Console.WriteLine(" --save-settings-file Save settings from JSON file to registry"); Console.WriteLine(" --help Show this help message"); - Console.WriteLine(" --version Show version information"); + Console.WriteLine(" --version, -V Show version information"); Console.WriteLine(" --status Show current installation status"); Console.WriteLine(" --clear-status Clear all installation status data"); Console.WriteLine(" --clear-cache Clear all caches including failed installation files (BootstrapMate + Chocolatey)"); @@ -481,6 +482,7 @@ static async Task MainAsync(string[] args) break; case "--verbose": + case "-v": // Verbose mode is already handled in Main() break; @@ -652,6 +654,7 @@ private static int SaveSettingsFromArgs(string[] args) config.SilentMode = true; break; case "--verbose": + case "-v": config.VerboseMode = true; break; case "--force": diff --git a/README.md b/README.md index d8a490b..6247162 100644 --- a/README.md +++ b/README.md @@ -251,8 +251,9 @@ HKLM\SOFTWARE\BootstrapMate\ BootstrapMate creates detailed logs: - **Location**: `C:\ProgramData\ManagedBootstrap\logs\` -- **Format**: `YYYY-MM-DD-HHmmss.log` -- **Content**: Detailed execution logs with timestamps +- **File name**: one file per run, `YYYY-MM-DD-HHmmss.log` +- **Line format**: `[yyyy-MM-dd HH:mm:ss] LEVEL message` in local time, where `LEVEL` is `DEBUG`, `INFO`, `WARN` or `ERROR` padded to five characters +- **Retention**: files older than 30 days are deleted at the start of each run ### Common Issues @@ -522,7 +523,8 @@ Options: --config Custom configuration file --phase Run specific phase (setupassistant, userland) --dry-run Test mode without actual installation - --verbose Enable detailed logging + --verbose, -v Enable detailed logging + --version, -V Print the version and exit --uninstall Remove service and cleanup --help Show help information ```