Add global unhandled exception handler to GUI

This commit is contained in:
whimsical-c4lic0 2026-02-26 22:55:50 -06:00
parent 6b345ea986
commit c035fa5721

View File

@ -1,4 +1,5 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Microsoft.Extensions.DependencyInjection;
using OF_DL.Helpers;
using OF_DL.Services;
@ -8,33 +9,98 @@ namespace OF_DL.Gui;
public static class Program
{
private static int s_hasProcessedUnhandledException;
public static bool HidePrivateInfo { get; private set; }
public static void Main(string[] args)
{
// Parse command line arguments
HidePrivateInfo = args.Contains("--hide-private-info", StringComparer.OrdinalIgnoreCase);
ServiceCollection services = new();
services.AddSingleton<ILoggingService, LoggingService>();
ServiceProvider tempProvider = services.BuildServiceProvider();
ILoggingService loggingService = tempProvider.GetRequiredService<ILoggingService>();
Log.Information("Starting OF DL GUI");
// Check if running in Docker and print a message
if (EnvironmentHelper.IsRunningInDocker())
try
{
Console.WriteLine(
"In your web browser, navigate to the port forwarded from your docker container. For instance, if your docker run command included \"-p 8080:8080\", open your web browser to \"http://localhost:8080\".");
}
// Parse command line arguments
HidePrivateInfo = args.Contains("--hide-private-info", StringComparer.OrdinalIgnoreCase);
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
ServiceCollection services = new();
services.AddSingleton<ILoggingService, LoggingService>();
ServiceProvider tempProvider = services.BuildServiceProvider();
ILoggingService loggingService = tempProvider.GetRequiredService<ILoggingService>();
RegisterGlobalExceptionHandlers();
Log.Information("Starting OF DL GUI");
// Check if running in Docker and print a message
if (EnvironmentHelper.IsRunningInDocker())
{
Console.WriteLine(
"In your web browser, navigate to the port forwarded from your docker container. For instance, if your docker run command included \"-p 8080:8080\", open your web browser to \"http://localhost:8080\".");
}
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
}
catch (Exception ex)
{
HandleUnhandledException(ex, "Program.Main", isTerminating: true);
}
finally
{
Log.CloseAndFlush();
}
}
private static AppBuilder BuildAvaloniaApp() =>
AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace();
private static void RegisterGlobalExceptionHandlers()
{
AppDomain.CurrentDomain.UnhandledException += (_, eventArgs) =>
{
HandleUnhandledException(eventArgs.ExceptionObject as Exception,
"AppDomain.CurrentDomain.UnhandledException",
eventArgs.IsTerminating);
};
TaskScheduler.UnobservedTaskException += (_, eventArgs) =>
{
HandleUnhandledException(eventArgs.Exception, "TaskScheduler.UnobservedTaskException",
isTerminating: false);
eventArgs.SetObserved();
};
}
private static void HandleUnhandledException(Exception? exception, string source, bool isTerminating)
{
if (Interlocked.Exchange(ref s_hasProcessedUnhandledException, 1) != 0)
{
return;
}
try
{
if (exception != null)
{
Log.Fatal(exception, "Unhandled exception from {Source}. Terminating={IsTerminating}",
source, isTerminating);
Console.WriteLine($"Unhandled exception from {source}: {exception}");
}
else
{
Log.Fatal("Unhandled non-exception object from {Source}. Terminating={IsTerminating}",
source, isTerminating);
Console.WriteLine($"Unhandled non-exception object from {source}.");
}
}
finally
{
Log.CloseAndFlush();
}
if (!isTerminating &&
Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
desktopLifetime.Shutdown(1);
}
}
}