Make logging more consistent

also added more verbosity, added -menu as an alias for -preferences, and added a migration for people using progress dialog
This commit is contained in:
pizzaboxer 2023-02-16 10:29:40 +00:00
parent 1d48657152
commit 2b646e2b55
8 changed files with 127 additions and 56 deletions

View File

@ -35,13 +35,14 @@ namespace Bloxstrap
public static bool IsUninstall { get; private set; } = false; public static bool IsUninstall { get; private set; } = false;
public static bool IsNoLaunch { get; private set; } = false; public static bool IsNoLaunch { get; private set; } = false;
public static bool IsUpgrade { get; private set; } = false; public static bool IsUpgrade { get; private set; } = false;
public static bool IsMenuLaunch { get; private set; } = false;
public static string[] LaunchArgs { get; private set; } = null!; public static string[] LaunchArgs { get; private set; } = null!;
public static string Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString()[..^2]; public static string Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString()[..^2];
// singletons // singletons
public static Logger Logger { get; private set; } = null!; public static readonly Logger Logger = new();
public static readonly JsonManager<Settings> Settings = new(); public static readonly JsonManager<Settings> Settings = new();
public static readonly JsonManager<State> State = new(); public static readonly JsonManager<State> State = new();
public static readonly HttpClient HttpClient = new(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All }); public static readonly HttpClient HttpClient = new(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All });
@ -63,10 +64,22 @@ namespace Bloxstrap
Environment.Exit(code); Environment.Exit(code);
} }
private void InitLog()
{
// if we're running for the first time or uninstalling, log to temp folder
// else, log to bloxstrap folder
string logdir = IsFirstRun || IsUninstall ? Path.Combine(Directories.LocalAppData, "Temp") : Path.Combine(Directories.Base, "Logs");
string timestamp = DateTime.UtcNow.ToString("yyyyMMdd'T'HHmmss'Z'");
Logger.Initialize(Path.Combine(logdir, $"{ProjectName}_{timestamp}.log"));
}
protected override void OnStartup(StartupEventArgs e) protected override void OnStartup(StartupEventArgs e)
{ {
base.OnStartup(e); base.OnStartup(e);
Logger.WriteLine($"[App::OnStartup] Starting {ProjectName} v{Version}");
// To customize application configuration such as set high DPI settings or default font, // To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration. // see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize(); ApplicationConfiguration.Initialize();
@ -78,25 +91,46 @@ namespace Bloxstrap
if (LaunchArgs.Length > 0) if (LaunchArgs.Length > 0)
{ {
if (Array.IndexOf(LaunchArgs, "-preferences") != -1 || Array.IndexOf(LaunchArgs, "-menu") != -1)
{
Logger.WriteLine("[App::OnStartup] Started with IsMenuLaunch flag");
IsMenuLaunch = true;
}
if (Array.IndexOf(LaunchArgs, "-quiet") != -1) if (Array.IndexOf(LaunchArgs, "-quiet") != -1)
{
Logger.WriteLine("[App::OnStartup] Started with IsQuiet flag");
IsQuiet = true; IsQuiet = true;
}
if (Array.IndexOf(LaunchArgs, "-uninstall") != -1) if (Array.IndexOf(LaunchArgs, "-uninstall") != -1)
{
Logger.WriteLine("[App::OnStartup] Started with IsUninstall flag");
IsUninstall = true; IsUninstall = true;
}
if (Array.IndexOf(LaunchArgs, "-nolaunch") != -1) if (Array.IndexOf(LaunchArgs, "-nolaunch") != -1)
{
Logger.WriteLine("[App::OnStartup] Started with IsNoLaunch flag");
IsNoLaunch = true; IsNoLaunch = true;
}
if (Array.IndexOf(LaunchArgs, "-upgrade") != -1) if (Array.IndexOf(LaunchArgs, "-upgrade") != -1)
{
Logger.WriteLine("[App::OnStartup] Bloxstrap started with IsUpgrade flag");
IsUpgrade = true; IsUpgrade = true;
} }
}
// check if installed // check if installed
RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"); using (RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"))
{
if (registryKey is null) if (registryKey is null)
{ {
Logger.WriteLine("[App::OnStartup] Running first-time install");
BaseDirectory = Path.Combine(Directories.LocalAppData, ProjectName); BaseDirectory = Path.Combine(Directories.LocalAppData, ProjectName);
InitLog();
if (!IsQuiet) if (!IsQuiet)
{ {
@ -108,23 +142,23 @@ namespace Bloxstrap
{ {
IsFirstRun = false; IsFirstRun = false;
BaseDirectory = (string)registryKey.GetValue("InstallLocation")!; BaseDirectory = (string)registryKey.GetValue("InstallLocation")!;
registryKey.Close(); }
} }
// exit if we don't click the install button on installation // exit if we don't click the install button on installation
if (!IsSetupComplete) if (!IsSetupComplete)
{
Logger.WriteLine("[App::OnStartup] Installation cancelled!");
Environment.Exit(Bootstrapper.ERROR_INSTALL_USEREXIT); Environment.Exit(Bootstrapper.ERROR_INSTALL_USEREXIT);
}
Directories.Initialize(BaseDirectory); Directories.Initialize(BaseDirectory);
string logdir = IsFirstRun || IsUninstall ? Path.Combine(Directories.LocalAppData, "Temp") : Path.Combine(Directories.Base, "Logs");
string timestamp = DateTime.UtcNow.ToString("yyyyMMdd'T'HHmmss'Z'");
Logger = new(Path.Combine(logdir, $"{ProjectName}_{timestamp}.log"));
// we shouldn't save settings on the first run until the first installation is finished, // we shouldn't save settings on the first run until the first installation is finished,
// just in case the user decides to cancel the install // just in case the user decides to cancel the install
if (!IsFirstRun) if (!IsFirstRun)
{ {
InitLog();
Settings.Load(); Settings.Load();
State.Load(); State.Load();
} }
@ -136,9 +170,7 @@ namespace Bloxstrap
string commandLine = ""; string commandLine = "";
if (LaunchArgs.Length > 0) if (IsMenuLaunch)
{
if (LaunchArgs[0] == "-preferences")
{ {
#if !DEBUG #if !DEBUG
if (Process.GetProcessesByName(ProjectName).Length > 1) if (Process.GetProcessesByName(ProjectName).Length > 1)
@ -150,7 +182,9 @@ namespace Bloxstrap
new MainWindow().ShowDialog(); new MainWindow().ShowDialog();
} }
else if (LaunchArgs[0].StartsWith("roblox-player:")) else if (LaunchArgs.Length > 0)
{
if (LaunchArgs[0].StartsWith("roblox-player:"))
{ {
commandLine = Protocol.ParseUri(LaunchArgs[0]); commandLine = Protocol.ParseUri(LaunchArgs[0]);
} }

View File

@ -427,7 +427,7 @@ namespace Bloxstrap
uninstallKey.SetValue("InstallLocation", Directories.Base); uninstallKey.SetValue("InstallLocation", Directories.Base);
uninstallKey.SetValue("NoRepair", 1); uninstallKey.SetValue("NoRepair", 1);
uninstallKey.SetValue("Publisher", "pizzaboxer"); uninstallKey.SetValue("Publisher", "pizzaboxer");
uninstallKey.SetValue("ModifyPath", $"\"{Directories.Application}\" -preferences"); uninstallKey.SetValue("ModifyPath", $"\"{Directories.Application}\" -menu");
uninstallKey.SetValue("QuietUninstallString", $"\"{Directories.Application}\" -uninstall -quiet"); uninstallKey.SetValue("QuietUninstallString", $"\"{Directories.Application}\" -uninstall -quiet");
uninstallKey.SetValue("UninstallString", $"\"{Directories.Application}\" -uninstall"); uninstallKey.SetValue("UninstallString", $"\"{Directories.Application}\" -uninstall");
uninstallKey.SetValue("URLInfoAbout", $"https://github.com/{App.ProjectRepository}"); uninstallKey.SetValue("URLInfoAbout", $"https://github.com/{App.ProjectRepository}");
@ -466,7 +466,7 @@ namespace Bloxstrap
ShellLink.Shortcut.CreateShortcut(Directories.Application, "", Directories.Application, 0) ShellLink.Shortcut.CreateShortcut(Directories.Application, "", Directories.Application, 0)
.WriteToFile(Path.Combine(Directories.StartMenu, "Play Roblox.lnk")); .WriteToFile(Path.Combine(Directories.StartMenu, "Play Roblox.lnk"));
ShellLink.Shortcut.CreateShortcut(Directories.Application, "-preferences", Directories.Application, 0) ShellLink.Shortcut.CreateShortcut(Directories.Application, "-menu", Directories.Application, 0)
.WriteToFile(Path.Combine(Directories.StartMenu, $"{App.ProjectName} Menu.lnk")); .WriteToFile(Path.Combine(Directories.StartMenu, $"{App.ProjectName} Menu.lnk"));
} }
else else
@ -479,7 +479,7 @@ namespace Bloxstrap
File.Delete(oldMenuShortcut); File.Delete(oldMenuShortcut);
if (!File.Exists(newMenuShortcut)) if (!File.Exists(newMenuShortcut))
ShellLink.Shortcut.CreateShortcut(Directories.Application, "-preferences", Directories.Application, 0) ShellLink.Shortcut.CreateShortcut(Directories.Application, "-menu", Directories.Application, 0)
.WriteToFile(newMenuShortcut); .WriteToFile(newMenuShortcut);
} }

View File

@ -50,40 +50,51 @@ namespace Bloxstrap.Helpers
}; };
#endregion #endregion
private static string BuildBaseUrl(string channel) private static string BuildBaseUrl(string channel) => channel == DefaultChannel ? DefaultBaseUrl : $"{DefaultBaseUrl}/channel/{channel.ToLower()}";
{
if (channel == DefaultChannel)
return DefaultBaseUrl;
else
return $"{DefaultBaseUrl}/channel/{channel.ToLower()}";
}
public static async Task<ClientVersion> GetLastDeploy(string channel, bool timestamp = false) public static async Task<ClientVersion> GetLastDeploy(string channel, bool timestamp = false)
{ {
App.Logger.WriteLine($"[DeployManager::GetLastDeploy] Getting deploy info for channel {channel} (timestamp={timestamp})");
HttpResponseMessage deployInfoResponse = await App.HttpClient.GetAsync($"https://clientsettings.roblox.com/v2/client-version/WindowsPlayer/channel/{channel}"); HttpResponseMessage deployInfoResponse = await App.HttpClient.GetAsync($"https://clientsettings.roblox.com/v2/client-version/WindowsPlayer/channel/{channel}");
string rawResponse = await deployInfoResponse.Content.ReadAsStringAsync();
if (!deployInfoResponse.IsSuccessStatusCode) if (!deployInfoResponse.IsSuccessStatusCode)
{ {
// 400 = Invalid binaryType. // 400 = Invalid binaryType.
// 404 = Could not find version details for binaryType. // 404 = Could not find version details for binaryType.
// 500 = Error while fetching version information. // 500 = Error while fetching version information.
// either way, we throw // either way, we throw
throw new Exception($"Could not get latest deploy for channel {channel}");
App.Logger.WriteLine(
"[DeployManager::GetLastDeploy] Failed to fetch deploy info!\r\n"+
$"\tStatus code: {deployInfoResponse.StatusCode}\r\n"+
$"\tResponse: {rawResponse}"
);
throw new Exception($"Could not get latest deploy for channel {channel}! (HTTP {deployInfoResponse.StatusCode})");
} }
string rawJson = await deployInfoResponse.Content.ReadAsStringAsync(); App.Logger.WriteLine($"[DeployManager::GetLastDeploy] Got JSON: {rawResponse}");
ClientVersion clientVersion = JsonSerializer.Deserialize<ClientVersion>(rawJson)!;
ClientVersion clientVersion = JsonSerializer.Deserialize<ClientVersion>(rawResponse)!;
// for preferences // for preferences
if (timestamp) if (timestamp)
{ {
App.Logger.WriteLine("[DeployManager::GetLastDeploy] Getting timestamp...");
string channelUrl = BuildBaseUrl(channel); string channelUrl = BuildBaseUrl(channel);
string manifestUrl = $"{channelUrl}/{clientVersion.VersionGuid}-rbxPkgManifest.txt";
// get an approximate deploy time from rbxpkgmanifest's last modified date // get an approximate deploy time from rbxpkgmanifest's last modified date
HttpResponseMessage pkgResponse = await App.HttpClient.GetAsync($"{channelUrl}/{clientVersion.VersionGuid}-rbxPkgManifest.txt"); HttpResponseMessage pkgResponse = await App.HttpClient.GetAsync(manifestUrl);
if (pkgResponse.Content.Headers.TryGetValues("last-modified", out var values)) if (pkgResponse.Content.Headers.TryGetValues("last-modified", out var values))
{ {
string lastModified = values.First(); string lastModified = values.First();
App.Logger.WriteLine($"[DeployManager::GetLastDeploy] {manifestUrl} - Last-Modified: {lastModified}");
clientVersion.Timestamp = DateTime.Parse(lastModified); clientVersion.Timestamp = DateTime.Parse(lastModified);
} }
} }

View File

@ -2,11 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Printing;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace Bloxstrap.Helpers namespace Bloxstrap.Helpers
{ {
@ -14,31 +11,49 @@ namespace Bloxstrap.Helpers
public class Logger public class Logger
{ {
private readonly SemaphoreSlim _semaphore = new(1, 1); private readonly SemaphoreSlim _semaphore = new(1, 1);
private readonly FileStream _filestream; private readonly List<string> _backlog = new();
private FileStream? _filestream;
public Logger(string filename) public void Initialize(string filename)
{ {
if (_filestream is not null)
throw new Exception("Logger is already initialized");
string? directory = Path.GetDirectoryName(filename); string? directory = Path.GetDirectoryName(filename);
if (directory is not null) if (directory is not null)
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
_filestream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.Read); _filestream = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.Read);
WriteLine($"[Logger::Logger] {App.ProjectName} v{App.Version} - Initialized at {filename}");
if (_backlog.Count > 0)
WriteToLog(String.Join("\r\n", _backlog));
WriteLine($"[Logger::Logger] Initialized at {filename}");
} }
public async void WriteLine(string message) public void WriteLine(string message)
{ {
string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'"); string timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'");
string conout = $"{timestamp} {message}"; string outcon = $"{timestamp} {message}";
byte[] fileout = Encoding.Unicode.GetBytes($"{conout.Replace(Directories.UserProfile, "<UserProfileFolder>")}\r\n"); string outlog = outcon.Replace(Directories.UserProfile, "<UserProfileFolder>");
Debug.WriteLine(conout); Debug.WriteLine(outcon);
WriteToLog(outlog);
}
private async void WriteToLog(string message)
{
if (_filestream is null)
{
_backlog.Add(message);
return;
}
try try
{ {
await _semaphore.WaitAsync(); await _semaphore.WaitAsync();
await _filestream.WriteAsync(fileout); await _filestream.WriteAsync(Encoding.Unicode.GetBytes($"{message}\r\n"));
await _filestream.FlushAsync(); await _filestream.FlushAsync();
} }
finally finally

View File

@ -2,7 +2,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows; using System.Windows;
using Bloxstrap.Enums;
using Bloxstrap.Views; using Bloxstrap.Views;
namespace Bloxstrap.Helpers namespace Bloxstrap.Helpers
@ -49,6 +49,10 @@ namespace Bloxstrap.Helpers
Bootstrapper.Register(); Bootstrapper.Register();
// make people using progress dialog auto switch over to fluent on upgrade
if (App.Version == "2.0.0" && App.Settings.Prop.BootstrapperStyle == BootstrapperStyle.ProgressDialog)
App.Settings.Prop.BootstrapperStyle = BootstrapperStyle.FluentDialog;
if (App.IsQuiet || isAutoUpgrade) if (App.IsQuiet || isAutoUpgrade)
return; return;

View File

@ -17,7 +17,7 @@
}, },
"Bloxstrap (Menu)": { "Bloxstrap (Menu)": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "-preferences" "commandLineArgs": "-menu"
} }
} }
} }

View File

@ -40,9 +40,14 @@ namespace Bloxstrap.ViewModels
OnPropertyChanged(nameof(ChannelDeployInfo)); OnPropertyChanged(nameof(ChannelDeployInfo));
ClientVersion info = await DeployManager.GetLastDeploy(channel, true); ClientVersion info = await DeployManager.GetLastDeploy(channel, true);
string? strTimestamp = info.Timestamp?.ToString("MM/dd/yyyy h:mm:ss tt", App.CultureFormat);
ChannelDeployInfo = new DeployInfo() { Version = info.Version, VersionGuid = info.VersionGuid, Timestamp = strTimestamp! }; ChannelDeployInfo = new DeployInfo
{
Version = info.Version,
VersionGuid = info.VersionGuid,
Timestamp = info.Timestamp?.ToString("MM/dd/yyyy h:mm:ss tt", App.CultureFormat)!
};
OnPropertyChanged(nameof(ChannelDeployInfo)); OnPropertyChanged(nameof(ChannelDeployInfo));
} }

View File

@ -19,6 +19,8 @@ namespace Bloxstrap.Views
public MainWindow() public MainWindow()
{ {
App.Logger.WriteLine("[MainWindow::MainWindow] Initializing menu");
DataContext = new MainWindowViewModel(this, _dialogService); DataContext = new MainWindowViewModel(this, _dialogService);
SetTheme(); SetTheme();
InitializeComponent(); InitializeComponent();