C# 中如何记录未捕获的异常?
C# 记录未捕获异常:订阅 AppDomain.UnhandledException 事件记录全局异常,TaskScheduler.UnobservedTaskException 处理异步任务异常。本文给出完整实现。
核心:在程序入口订阅 AppDomain.CurrentDomain.UnhandledException 事件,在回调中用日志框架(如 NLog/log4net)记录异常详情,尽力在进程终止前留下错误信息,但不能保证缓冲日志全部落盘。
完整实现
using System;
using System.Threading.Tasks;
using NLog;
class Program
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
static void Main(string[] args)
{
// 订阅全局未捕获异常事件
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
var ex = e.ExceptionObject as Exception;
Logger.Fatal(ex, "未捕获的异常导致程序崩溃");
};
// 捕获异步任务中未观察到的异常
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
Logger.Error(e.Exception, "异步任务中出现未处理的异常");
e.SetObserved(); // 标记为已观察;现代 .NET 默认不会因未观察任务异常终止
};
// 正常业务逻辑...
}
}
ASP.NET Core 的方式
// ASP.NET Core Program.cs;app 为 WebApplication
// 需引入 Microsoft.AspNetCore.Diagnostics、Microsoft.AspNetCore.Http
// 与 Microsoft.Extensions.Logging
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerFeature>();
app.Logger.LogError(feature?.Error, "未处理的请求异常");
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
await context.Response.WriteAsync("服务器内部错误");
});
});
常见问题(FAQ)
Q:.NET Core / .NET 5+ 还需要 AppDomain 事件吗?
A:仍然有效,但 ASP.NET Core 更推荐用中间件(ExceptionHandlerMiddleware)统一处理请求管道中的异常。
Q:async/await 中的异常能被 UnhandledException 捕获吗?
A:async Task 的异常保存在 Task 中,await 时重新抛出,应在调用边界捕获。未观察异常事件可能延迟到垃圾回收时发生,不能替代 await;async void 的传播方式不同,也可能成为未处理异常。
Q:记录完异常后程序会继续运行吗?
A:UnhandledException 触发时进程已经在终止过程中,你只能记录日志、不能做恢复操作。现代 .NET 默认不会因未观察 Task 异常终止;SetObserved() 用于标记已观察,旧运行时或显式升级策略需另行核对。