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
```