Basic Usage Examples
This section provides practical examples of how to use Plugin.ExceptionListeners in common scenarios.
Console Application Example
Simple Exception Monitoring
using Plugin.ExceptionListeners;
using Plugin.ExceptionListeners.Listeners;
namespace BasicConsoleApp
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Starting exception monitoring...");
// Set up exception listeners
using var unhandledListener = new CurrentDomainUnhandledExceptionListener(HandleUnhandledException);
using var taskListener = new TaskSchedulerUnobservedTaskExceptionListener(HandleTaskException);
Console.WriteLine("Exception listeners active. Running test scenarios...");
// Test handled exceptions (won't trigger unhandled listener)
await TestHandledExceptions();
// Test unobserved task exceptions
await TestUnobservedTaskExceptions();
Console.WriteLine("Tests completed. Press any key to exit...");
Console.ReadKey();
}
private static void HandleUnhandledException(object? sender, ExceptionEventArgs e)
{
Console.WriteLine($"[CRITICAL] Unhandled exception: {e.Exception.GetType().Name}");
Console.WriteLine($"Message: {e.Exception.Message}");
// In a real application, you would:
// - Log to file/database
// - Send to monitoring service
// - Perform cleanup operations
LogToFile("UNHANDLED", e.Exception);
}
private static void HandleTaskException(object? sender, ExceptionEventArgs e)
{
Console.WriteLine($"[WARNING] Unobserved task exception: {e.Exception.GetType().Name}");
Console.WriteLine($"Message: {e.Exception.Message}");
LogToFile("TASK", e.Exception);
}
private static async Task TestHandledExceptions()
{
Console.WriteLine("\n--- Testing Handled Exceptions ---");
// These exceptions are caught and handled, so won't trigger unhandled listener
for (int i = 0; i < 3; i++)
{
try
{
throw new InvalidOperationException($"Test exception {i + 1}");
}
catch (Exception ex)
{
Console.WriteLine($"Caught and handled: {ex.Message}");
}
}
}
private static async Task TestUnobservedTaskExceptions()
{
Console.WriteLine("\n--- Testing Unobserved Task Exceptions ---");
// Create tasks that fail but don't await them
for (int i = 0; i < 3; i++)
{
var taskNumber = i + 1;
Task.Run(async () =>
{
await Task.Delay(50);
throw new InvalidDataException($"Unobserved task exception {taskNumber}");
});
// Not awaiting these tasks makes their exceptions "unobserved"
}
// Give tasks time to fail
await Task.Delay(200);
// Force garbage collection to trigger unobserved task exception events
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// Give time for the events to be processed
await Task.Delay(100);
}
private static void LogToFile(string type, Exception exception)
{
try
{
var logEntry = $"{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} [{type}] {exception.GetType().Name}: {exception.Message}\n";
File.AppendAllText("exceptions.log", logEntry);
}
catch
{
// Don't let logging failures crash the application
}
}
}
}
Web Application Example
ASP.NET Core Integration
// Program.cs
using Plugin.ExceptionListeners;
using Plugin.ExceptionListeners.Listeners;
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddControllers();
builder.Services.AddSingleton<IExceptionHandlingService, ExceptionHandlingService>();
var app = builder.Build();
// Initialize exception handling
var exceptionService = app.Services.GetRequiredService<IExceptionHandlingService>();
exceptionService.Initialize();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseRouting();
app.MapControllers();
app.Run();
// Exception handling service
public interface IExceptionHandlingService
{
void Initialize();
}
public class ExceptionHandlingService : IExceptionHandlingService, IDisposable
{
private readonly ILogger<ExceptionHandlingService> _logger;
private readonly List<ExceptionListener> _listeners = new();
public ExceptionHandlingService(ILogger<ExceptionHandlingService> logger)
{
_logger = logger;
}
public void Initialize()
{
_logger.LogInformation("Initializing exception handling...");
// Set up listeners
_listeners.Add(new CurrentDomainUnhandledExceptionListener(HandleUnhandledException));
_listeners.Add(new TaskSchedulerUnobservedTaskExceptionListener(HandleTaskException));
_logger.LogInformation("Exception handling initialized with {Count} listeners", _listeners.Count);
}
private void HandleUnhandledException(object? sender, ExceptionEventArgs e)
{
_logger.LogCritical(e.Exception, "Unhandled exception occurred in web application");
// In production, you might want to:
// - Send to Application Insights
// - Report to Sentry
// - Send alert notifications
// - Save critical application state
}
private void HandleTaskException(object? sender, ExceptionEventArgs e)
{
_logger.LogError(e.Exception, "Unobserved task exception in web application");
// Task exceptions are automatically marked as observed
// This prevents them from crashing the application
}
public void Dispose()
{
_logger.LogInformation("Disposing exception handling service...");
foreach (var listener in _listeners)
{
listener.Dispose();
}
_listeners.Clear();
}
}
// Example controller
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private readonly ILogger<TestController> _logger;
public TestController(ILogger<TestController> logger)
{
_logger = logger;
}
[HttpGet("handled-exception")]
public IActionResult TestHandledException()
{
try
{
throw new InvalidOperationException("This exception will be handled normally");
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Handled exception in controller");
return BadRequest(new { error = ex.Message });
}
}
[HttpGet("unobserved-task")]
public IActionResult TestUnobservedTask()
{
// Create a fire-and-forget task that will fail
Task.Run(async () =>
{
await Task.Delay(100);
throw new InvalidDataException("This will become an unobserved task exception");
});
return Ok(new { message = "Unobserved task started - check logs for exception" });
}
[HttpGet("background-work")]
public IActionResult TestBackgroundWork()
{
// Proper way to handle background tasks
_ = Task.Run(async () =>
{
try
{
await DoBackgroundWork();
}
catch (Exception ex)
{
_logger.LogError(ex, "Background work failed");
}
});
return Ok(new { message = "Background work started" });
}
private async Task DoBackgroundWork()
{
await Task.Delay(200);
// Simulate work that might fail
if (Random.Shared.Next(2) == 0)
{
throw new InvalidOperationException("Simulated background work failure");
}
}
}
Windows Service Example
Background Service with Exception Handling
using Microsoft.Extensions.Hosting;
using Plugin.ExceptionListeners;
using Plugin.ExceptionListeners.Listeners;
// Program.cs
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = "ExceptionListenerService";
});
builder.Services.AddHostedService<ExceptionMonitoringService>();
builder.Services.AddSingleton<IExceptionReportingService, ExceptionReportingService>();
var host = builder.Build();
host.Run();
// Main service
public class ExceptionMonitoringService : BackgroundService
{
private readonly ILogger<ExceptionMonitoringService> _logger;
private readonly IExceptionReportingService _reportingService;
private readonly List<ExceptionListener> _listeners = new();
public ExceptionMonitoringService(
ILogger<ExceptionMonitoringService> logger,
IExceptionReportingService reportingService)
{
_logger = logger;
_reportingService = reportingService;
}
public override Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting exception monitoring service...");
// Initialize exception listeners
_listeners.Add(new CurrentDomainUnhandledExceptionListener(HandleUnhandledException));
_listeners.Add(new TaskSchedulerUnobservedTaskExceptionListener(HandleTaskException));
_logger.LogInformation("Exception listeners initialized");
return base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Exception monitoring service is running");
// Main service loop
while (!stoppingToken.IsCancellationRequested)
{
try
{
// Simulate service work
await DoServiceWork(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in service main loop");
}
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
private async Task DoServiceWork(CancellationToken cancellationToken)
{
// Simulate various service operations that might fail
var tasks = new[]
{
ProcessDataAsync(cancellationToken),
CheckSystemHealthAsync(cancellationToken),
CleanupOldFilesAsync(cancellationToken)
};
// Process tasks and handle any failures
var results = await Task.WhenAll(tasks.Select(async task =>
{
try
{
await task;
return new { Success = true, Error = (Exception?)null };
}
catch (Exception ex)
{
return new { Success = false, Error = ex };
}
}));
// Log any failures
foreach (var result in results.Where(r => !r.Success))
{
_logger.LogError(result.Error, "Service task failed");
}
}
private async Task ProcessDataAsync(CancellationToken cancellationToken)
{
// Simulate data processing
await Task.Delay(100, cancellationToken);
// Occasionally throw an exception to test handling
if (Random.Shared.Next(20) == 0)
{
throw new InvalidDataException("Simulated data processing error");
}
}
private async Task CheckSystemHealthAsync(CancellationToken cancellationToken)
{
await Task.Delay(50, cancellationToken);
// Health check logic
}
private async Task CleanupOldFilesAsync(CancellationToken cancellationToken)
{
await Task.Delay(75, cancellationToken);
// File cleanup logic
}
private void HandleUnhandledException(object? sender, ExceptionEventArgs e)
{
_logger.LogCritical(e.Exception, "CRITICAL: Unhandled exception in service");
// Report critical errors
_ = Task.Run(async () =>
{
try
{
await _reportingService.ReportCriticalErrorAsync(e.Exception);
}
catch (Exception reportingEx)
{
_logger.LogError(reportingEx, "Failed to report critical error");
}
});
}
private void HandleTaskException(object? sender, ExceptionEventArgs e)
{
_logger.LogError(e.Exception, "Unobserved task exception in service");
// Report async errors
_ = Task.Run(async () =>
{
try
{
await _reportingService.ReportAsyncErrorAsync(e.Exception);
}
catch
{
// Ignore reporting failures for task exceptions
}
});
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stopping exception monitoring service...");
// Clean up listeners
foreach (var listener in _listeners)
{
listener.Dispose();
}
_listeners.Clear();
await base.StopAsync(cancellationToken);
_logger.LogInformation("Exception monitoring service stopped");
}
}
// Exception reporting service
public interface IExceptionReportingService
{
Task ReportCriticalErrorAsync(Exception exception);
Task ReportAsyncErrorAsync(Exception exception);
}
public class ExceptionReportingService : IExceptionReportingService
{
private readonly ILogger<ExceptionReportingService> _logger;
private readonly HttpClient _httpClient;
public ExceptionReportingService(ILogger<ExceptionReportingService> logger)
{
_logger = logger;
_httpClient = new HttpClient();
}
public async Task ReportCriticalErrorAsync(Exception exception)
{
try
{
// Example: Send to monitoring service
var report = new
{
Type = "Critical",
Exception = exception.ToString(),
Timestamp = DateTimeOffset.UtcNow,
MachineName = Environment.MachineName,
ServiceName = "ExceptionListenerService"
};
// Send to your monitoring endpoint
// await _httpClient.PostAsJsonAsync("https://your-monitoring-service.com/api/errors", report);
_logger.LogInformation("Critical error reported successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to report critical error");
}
}
public async Task ReportAsyncErrorAsync(Exception exception)
{
try
{
var report = new
{
Type = "AsyncError",
Exception = exception.ToString(),
Timestamp = DateTimeOffset.UtcNow,
MachineName = Environment.MachineName
};
// Send to monitoring service
// await _httpClient.PostAsJsonAsync("https://your-monitoring-service.com/api/errors", report);
_logger.LogDebug("Async error reported successfully");
}
catch
{
// Silent failure for async error reporting
}
}
}
Desktop Application Example (WPF)
WPF Application with Exception Handling
// App.xaml.cs
using System.Windows;
using Plugin.ExceptionListeners;
using Plugin.ExceptionListeners.Listeners;
namespace WpfExceptionExample
{
public partial class App : Application
{
private readonly List<ExceptionListener> _listeners = new();
protected override void OnStartup(StartupEventArgs e)
{
// Set up exception handling before starting the UI
SetupExceptionHandling();
base.OnStartup(e);
}
private void SetupExceptionHandling()
{
// Handle unhandled exceptions
_listeners.Add(new CurrentDomainUnhandledExceptionListener(HandleUnhandledException));
_listeners.Add(new TaskSchedulerUnobservedTaskExceptionListener(HandleTaskException));
// Also handle WPF-specific unhandled exceptions
this.DispatcherUnhandledException += OnDispatcherUnhandledException;
}
private void HandleUnhandledException(object? sender, ExceptionEventArgs e)
{
LogException("Unhandled", e.Exception);
// Show user-friendly error message
ShowErrorDialog("A critical error occurred", e.Exception);
}
private void HandleTaskException(object? sender, ExceptionEventArgs e)
{
LogException("Task", e.Exception);
// For task exceptions, just log - don't show UI
}
private void OnDispatcherUnhandledException(object sender,
System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
LogException("Dispatcher", e.Exception);
// Show error dialog and mark as handled to prevent crash
ShowErrorDialog("An error occurred in the user interface", e.Exception);
e.Handled = true;
}
private void LogException(string type, Exception exception)
{
try
{
var logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{type}] {exception}\n\n";
File.AppendAllText("application.log", logEntry);
}
catch
{
// Ignore logging failures
}
}
private void ShowErrorDialog(string title, Exception exception)
{
// Use dispatcher to ensure we're on UI thread
Dispatcher.Invoke(() =>
{
var message = $"Error: {exception.Message}\n\nThe error has been logged. " +
"Please contact support if the problem persists.";
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Error);
});
}
protected override void OnExit(ExitEventArgs e)
{
// Clean up exception listeners
foreach (var listener in _listeners)
{
listener.Dispose();
}
_listeners.Clear();
base.OnExit(e);
}
}
}
// MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TestHandledException_Click(object sender, RoutedEventArgs e)
{
try
{
throw new InvalidOperationException("This exception will be handled");
}
catch (Exception ex)
{
MessageBox.Show($"Caught exception: {ex.Message}", "Handled Exception",
MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private void TestUnhandledException_Click(object sender, RoutedEventArgs e)
{
// This will be caught by the DispatcherUnhandledException handler
throw new InvalidOperationException("This will be an unhandled exception on the UI thread");
}
private void TestTaskException_Click(object sender, RoutedEventArgs e)
{
// Create an unobserved task exception
Task.Run(async () =>
{
await Task.Delay(100);
throw new InvalidDataException("This will be an unobserved task exception");
});
MessageBox.Show("Unobserved task created - check logs", "Task Exception Test",
MessageBoxButton.OK, MessageBoxImage.Information);
}
private async void TestAsyncWork_Click(object sender, RoutedEventArgs e)
{
try
{
await DoAsyncWork();
MessageBox.Show("Async work completed successfully", "Success",
MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Async work failed: {ex.Message}", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private async Task DoAsyncWork()
{
await Task.Delay(1000); // Simulate work
// Randomly fail to demonstrate exception handling
if (Random.Shared.Next(2) == 0)
{
throw new InvalidOperationException("Simulated async work failure");
}
}
}
Configuration Examples
JSON Configuration
// appsettings.json
{
"ExceptionHandling": {
"Enabled": true,
"EnableFirstChanceListener": false,
"EnableUnhandledListener": true,
"EnableTaskListener": true,
"EnableNativeListener": true,
"LogLevel": "Information",
"ReportingEndpoint": "https://your-monitoring-service.com/api/errors",
"MaxReportsPerMinute": 60,
"IgnoredExceptionTypes": [
"System.OperationCanceledException",
"System.Threading.Tasks.TaskCanceledException"
]
}
}
Configuration Service
public class ExceptionHandlingConfiguration
{
public bool Enabled { get; set; } = true;
public bool EnableFirstChanceListener { get; set; } = false;
public bool EnableUnhandledListener { get; set; } = true;
public bool EnableTaskListener { get; set; } = true;
public bool EnableNativeListener { get; set; } = true;
public string LogLevel { get; set; } = "Information";
public string? ReportingEndpoint { get; set; }
public int MaxReportsPerMinute { get; set; } = 60;
public List<string> IgnoredExceptionTypes { get; set; } = new();
}
public class ConfigurableExceptionService
{
private readonly ExceptionHandlingConfiguration _config;
private readonly ILogger<ConfigurableExceptionService> _logger;
private readonly List<ExceptionListener> _listeners = new();
public ConfigurableExceptionService(
IOptions<ExceptionHandlingConfiguration> config,
ILogger<ConfigurableExceptionService> logger)
{
_config = config.Value;
_logger = logger;
}
public void Initialize()
{
if (!_config.Enabled)
{
_logger.LogInformation("Exception handling is disabled by configuration");
return;
}
if (_config.EnableUnhandledListener)
{
_listeners.Add(new CurrentDomainUnhandledExceptionListener(HandleUnhandledException));
_logger.LogInformation("Unhandled exception listener enabled");
}
if (_config.EnableTaskListener)
{
_listeners.Add(new TaskSchedulerUnobservedTaskExceptionListener(HandleTaskException));
_logger.LogInformation("Task exception listener enabled");
}
if (_config.EnableFirstChanceListener)
{
_listeners.Add(new CurrentDomainFirstChanceExceptionListener(HandleFirstChanceException));
_logger.LogWarning("First-chance exception listener enabled - monitor performance");
}
_logger.LogInformation("Exception handling initialized with {Count} listeners", _listeners.Count);
}
private void HandleUnhandledException(object? sender, ExceptionEventArgs e)
{
if (ShouldIgnoreException(e.Exception))
return;
_logger.LogCritical(e.Exception, "Unhandled exception occurred");
}
private void HandleTaskException(object? sender, ExceptionEventArgs e)
{
if (ShouldIgnoreException(e.Exception))
return;
_logger.LogError(e.Exception, "Unobserved task exception occurred");
}
private void HandleFirstChanceException(object? sender, ExceptionEventArgs e)
{
if (ShouldIgnoreException(e.Exception))
return;
// Minimal processing for first-chance exceptions
_logger.LogDebug("First-chance exception: {ExceptionType}", e.Exception.GetType().Name);
}
private bool ShouldIgnoreException(Exception exception)
{
return _config.IgnoredExceptionTypes.Contains(exception.GetType().FullName);
}
}
These examples demonstrate various ways to integrate Plugin.ExceptionListeners into different types of applications, from simple console apps to complex web services and desktop applications.