⚠️ Plugin.ExceptionListeners Documentation
A comprehensive exception listening framework for .NET applications, providing unified exception handling across different exception sources and MAUI platforms.
📖 Overview
Plugin.ExceptionListeners is a powerful and flexible library that enables centralized exception handling in .NET applications. It provides a set of listener classes that can capture exceptions from various sources including:
- First-chance exceptions - Catch exceptions as they occur, before they're handled
- Unhandled exceptions - Catch exceptions that would otherwise terminate your application
- Unobserved task exceptions - Catch exceptions from Tasks that haven't been properly awaited
- Native platform exceptions (MAUI) - Catch native exceptions from iOS, Android, macOS, and Windows
🚀 Quick Start
Installation
Install the base package for standard .NET applications:
dotnet add package Plugin.ExceptionListeners
For MAUI applications, also install:
dotnet add package Plugin.ExceptionListeners.Maui
Basic Usage
using Plugin.ExceptionListeners.Listeners;
// Set up a global exception handler
void HandleException(object? sender, ExceptionEventArgs e)
{
Console.WriteLine($"Exception caught: {e.Exception.Message}");
// Log, report, or handle the exception as needed
}
// Listen for first-chance exceptions
using var firstChanceListener = new CurrentDomainFirstChanceExceptionListener(HandleException);
// Listen for unhandled exceptions
using var unhandledListener = new CurrentDomainUnhandledExceptionListener(HandleException);
// Listen for unobserved task exceptions
using var taskListener = new TaskSchedulerUnobservedTaskExceptionListener(HandleException);
// Your application code here...
MAUI Usage
using Plugin.ExceptionListeners.Maui;
// In your MAUI application (e.g., App.xaml.cs)
public partial class App : Application
{
private NativeUnhandledExceptionListener? _nativeListener;
public App()
{
InitializeComponent();
// Set up native exception handling
_nativeListener = new NativeUnhandledExceptionListener(HandleNativeException);
MainPage = new AppShell();
}
private void HandleNativeException(object? sender, ExceptionEventArgs e)
{
// Handle native exceptions from iOS/Android/Windows/macOS
System.Diagnostics.Debug.WriteLine($"Native exception: {e.Exception}");
}
protected override void OnSleep()
{
_nativeListener?.Dispose();
base.OnSleep();
}
}
📚 Core Components
Exception Listeners
All exception listeners inherit from the base ExceptionListener class and follow a consistent pattern:
- Automatic subscription - Listeners automatically subscribe to their respective exception sources upon creation
- Event-driven - All exceptions are propagated through a unified
Receivedevent - Disposable - Proper cleanup and unsubscription when disposed
- Thread-safe - Safe to use across multiple threads
Available Listeners
| Listener | Source | Platform Support |
|---|---|---|
CurrentDomainFirstChanceExceptionListener |
AppDomain.CurrentDomain.FirstChanceException |
All .NET platforms |
CurrentDomainUnhandledExceptionListener |
AppDomain.CurrentDomain.UnhandledException |
All .NET platforms |
TaskSchedulerUnobservedTaskExceptionListener |
TaskScheduler.UnobservedTaskException |
All .NET platforms |
NativeUnhandledExceptionListener |
Platform-specific native exceptions | MAUI only |
🎯 Use Cases
Comprehensive Exception Monitoring
public class ExceptionMonitoringService : IDisposable
{
private readonly List<ExceptionListener> _listeners = new();
public ExceptionMonitoringService()
{
// Set up comprehensive exception monitoring
_listeners.Add(new CurrentDomainFirstChanceExceptionListener(LogException));
_listeners.Add(new CurrentDomainUnhandledExceptionListener(LogCriticalException));
_listeners.Add(new TaskSchedulerUnobservedTaskExceptionListener(LogTaskException));
}
private void LogException(object? sender, ExceptionEventArgs e)
{
// Log all exceptions for diagnostics
Logger.Information("First-chance exception: {Exception}", e.Exception);
}
private void LogCriticalException(object? sender, ExceptionEventArgs e)
{
// Log critical unhandled exceptions
Logger.Fatal("Unhandled exception: {Exception}", e.Exception);
}
private void LogTaskException(object? sender, ExceptionEventArgs e)
{
// Log unobserved task exceptions
Logger.Error("Unobserved task exception: {Exception}", e.Exception);
}
public void Dispose()
{
foreach (var listener in _listeners)
{
listener.Dispose();
}
_listeners.Clear();
}
}
Exception Reporting and Analytics
public class ExceptionReportingService
{
public void SetupReporting()
{
var reporter = new CurrentDomainUnhandledExceptionListener(ReportException);
// Keep reference to prevent disposal
// Store in a field or dependency injection container
}
private async void ReportException(object? sender, ExceptionEventArgs e)
{
try
{
// Report to your preferred service (AppCenter, Sentry, etc.)
await ReportToAnalyticsService(e.Exception);
}
catch
{
// Prevent exceptions in exception handlers from crashing the app
}
}
private async Task ReportToAnalyticsService(Exception exception)
{
// Implementation depends on your analytics service
// This is just a placeholder
await Task.CompletedTask;
}
}
⚡ Advanced Features
Custom Exception Listeners
You can create custom exception listeners by inheriting from ExceptionListener:
public class CustomExceptionListener : ExceptionListener
{
public CustomExceptionListener(EventHandler<ExceptionEventArgs> received) : base(received)
{
// Subscribe to your custom exception source
MyCustomExceptionSource.ExceptionOccurred += OnCustomException;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Unsubscribe from your custom exception source
MyCustomExceptionSource.ExceptionOccurred -= OnCustomException;
}
base.Dispose(disposing);
}
private void OnCustomException(object? sender, CustomExceptionEventArgs e)
{
OnReceived(sender, e.Exception);
}
}
Exception Filtering and Transformation
public class FilteringExceptionHandler
{
private readonly HashSet<Type> _ignoredExceptionTypes = new()
{
typeof(OperationCanceledException),
typeof(TaskCanceledException)
};
public void SetupFiltering()
{
var listener = new CurrentDomainFirstChanceExceptionListener(FilterAndHandle);
}
private void FilterAndHandle(object? sender, ExceptionEventArgs e)
{
// Skip known harmless exceptions
if (_ignoredExceptionTypes.Contains(e.Exception.GetType()))
return;
// Handle only specific exceptions
switch (e.Exception)
{
case HttpRequestException httpEx:
HandleNetworkException(httpEx);
break;
case UnauthorizedAccessException authEx:
HandleAuthException(authEx);
break;
default:
HandleGenericException(e.Exception);
break;
}
}
private void HandleNetworkException(HttpRequestException ex) { /* ... */ }
private void HandleAuthException(UnauthorizedAccessException ex) { /* ... */ }
private void HandleGenericException(Exception ex) { /* ... */ }
}
🏗️ Architecture
Design Principles
- Single Responsibility - Each listener handles one specific exception source
- Open/Closed - Extensible through inheritance, closed for modification
- Dependency Inversion - Depends on abstractions, not concretions
- Resource Management - Proper cleanup through IDisposable pattern
Threading Considerations
- All listeners are thread-safe for subscription and disposal
- Exception events may be raised on different threads depending on the source
- Ensure your exception handlers are thread-safe or use appropriate synchronization
Performance Impact
- First-chance exceptions: High frequency, minimal processing recommended
- Unhandled exceptions: Low frequency, comprehensive logging acceptable
- Task exceptions: Medium frequency, depends on application async patterns
- Native exceptions: Platform-dependent, typically low frequency
🔧 Development & Testing
- Framework: .NET 9.0
- Testing: xUnit + FluentAssertions with comprehensive coverage
- Build:
dotnet build -c Release - Test:
dotnet test -c Release - Documentation: DocFX for API documentation
📄 License
This library is licensed under the MIT License - see the LICENSE.md file for details.
🤝 Contributing
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests on GitHub.
Development Setup
- Clone the repository
- Ensure .NET 9.0 SDK is installed
- Run
dotnet restoreto restore packages - Run
dotnet buildto build the solution - Run
dotnet testto execute tests
🔗 Related Resources
Built with ❤️ for modern .NET applications requiring comprehensive exception handling.