Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 52 additions & 32 deletions Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand All @@ -153,15 +153,35 @@ private static void Log(LogLevel level, string message)
WriteToPipe(level, message);
}

private static void WriteToFile(string message)
/// <summary>
/// Formats one log-file line: <c>[yyyy-MM-dd HH:mm:ss] LEVEL message</c> 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.
/// </summary>
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
{
Expand Down Expand Up @@ -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} ══");
Expand All @@ -239,51 +259,51 @@ 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}");
}

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}");
}

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}");
}
Expand All @@ -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}");
Expand Down Expand Up @@ -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");
}
}
}
13 changes: 8 additions & 5 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand Down Expand Up @@ -421,7 +422,7 @@ static async Task<int> MainAsync(string[] args)
Console.WriteLine("Options:");
Console.WriteLine(" --url <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");
Expand All @@ -430,7 +431,7 @@ static async Task<int> MainAsync(string[] args)
Console.WriteLine(" --save-settings Save GUI settings to registry");
Console.WriteLine(" --save-settings-file <path> 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)");
Expand Down Expand Up @@ -481,6 +482,7 @@ static async Task<int> MainAsync(string[] args)
break;

case "--verbose":
case "-v":
// Verbose mode is already handled in Main()
break;

Expand Down Expand Up @@ -652,6 +654,7 @@ private static int SaveSettingsFromArgs(string[] args)
config.SilentMode = true;
break;
case "--verbose":
case "-v":
config.VerboseMode = true;
break;
case "--force":
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -522,7 +523,8 @@ Options:
--config <path> Custom configuration file
--phase <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
```
Expand Down
Loading