Implementeer globale exception handling
Elke applicatie hoort globale exception handling te hebben. Niet omdat je verwacht dat er iets misgaat, maar omdat er altijd iets misgaat op een onverwachte manier. Zonder globale exception handling heb je geen registratie van wat er is gebeurd, en geen controle over hoe de applicatie reageert.
Er zijn twee categorieën exceptions om af te handelen: verwachte fouten en onverwachte exceptions. Verwachte fouten zijn dingen waar je op anticipeert — ongeldige invoer, een mislukte HTTP-call, een ontbrekend bestand. Die kun je het beste afhandelen dicht bij de plek waar ze ontstaan. Onverwachte exceptions zijn de exceptions die je niet hebt voorzien — bugs, null references, fouten in third-party libraries. Daarvoor heb je een globaal vangnet nodig.
Globale exception handling hoort thuis op het instappunt van je applicatie. Welke hooks je gebruikt hangt af van je presentatielaag, maar de bedoeling is altijd dezelfde: log wat er is gebeurd en beslis vervolgens of de applicatie door kan gaan.
Log onverwachte exceptions als Fatal wanneer de
applicatie niet kan herstellen, en als Error wanneer dat
wel kan. Een onbekende applicatiestaat is gevaarlijk — het is vaak beter
om netjes te crashen en opnieuw op te starten dan door te gaan in een
mogelijk corrupte staat.
In plaats van geen globale handler te hebben en de exception volledig kwijt te raken, voeg je de juiste handlers voor jouw platform toe.
ASP.NET Core API
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerFeature>();
Log.Fatal(feature?.Error, "Unhandled exception");
context.Response.StatusCode = 500;
});
});Avalonia
// Program.cs
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
Log.Fatal(e.ExceptionObject as Exception, "Unhandled exception");
TaskScheduler.UnobservedTaskException += (_, e) =>
{
Log.Error(e.Exception, "Unobserved task exception");
e.SetObserved();
};
// App.axaml.cs
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.UnhandledException += (_, e) =>
{
Log.Fatal(e.Exception, "UI thread exception");
e.Handled = true;
};UnhandledException wordt afgevuurd wanneer de applicatie
al aan het afsluiten is — log als Fatal.
UnobservedTaskException wordt afgevuurd wanneer een
gefaalde Task door de garbage collector wordt opgeruimd
zonder dat hij is geobserveerd — de applicatie blijft draaien, dus log
als Error en roep SetObserved() aan om de
crash te voorkomen.
MAUI
MauiExceptions.UnhandledException += (_, e) =>
Log.Fatal(e.ExceptionObject as Exception, "Unhandled exception");
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
Log.Fatal(e.ExceptionObject as Exception, "Unhandled exception");
TaskScheduler.UnobservedTaskException += (_, e) =>
{
Log.Error(e.Exception, "Unobserved task exception");
e.SetObserved();
};WPF
// App.xaml.cs
Application.Current.DispatcherUnhandledException += (_, e) =>
{
Log.Fatal(e.Exception, "UI thread exception");
e.Handled = true;
};
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
Log.Fatal(e.ExceptionObject as Exception, "Unhandled exception");
TaskScheduler.UnobservedTaskException += (_, e) =>
{
Log.Error(e.Exception, "Unobserved task exception");
e.SetObserved();
};CLI
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
Log.Fatal(e.ExceptionObject as Exception, "Unhandled exception");
TaskScheduler.UnobservedTaskException += (_, e) =>
{
Log.Error(e.Exception, "Unobserved task exception");
e.SetObserved();
};AppDomain.CurrentDomain.UnhandledException en
TaskScheduler.UnobservedTaskException gelden voor alle
platforms en horen altijd aanwezig te zijn. De platformspecifieke hook
handelt UI-thread exceptions af voor applicaties die een UI-thread
hebben.
Globale exception handling vervangt niet het afhandelen van verwachte fouten dicht bij de plek waar ze ontstaan. Het is het vangnet voor al het andere.
Referenties
- AppDomain.UnhandledException - Microsoft Learn
- TaskScheduler.UnobservedTaskException - Microsoft Learn
- Error handling in ASP.NET Core - Microsoft Learn