You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 未捕获异常日志来源及非手动捕获式处理方案咨询

问题描述

我有多款运行在Docker容器中的.NET 6.0 Web应用,使用Serilog作为日志库,通过builder.Host.UseSerilog()配置,并在类中通过依赖注入使用Microsoft.Extensions.Logging.ILogger<out TCategoryName>。当发生未处理的错误时,即便未在此处实现日志记录语句,容器的Docker日志中仍会出现错误信息。该信息格式与Serilog常规输出不同,似乎是.NET内置功能生成的,请问:

  1. 这些错误信息来自哪里?
  2. 是否可以将这类消息重定向,例如记录到数据库或通过HTTP Post发送至其他服务?

另外,这种情况在简单控制台应用中也存在:

namespace ConsoleApp1;

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
        throw new InvalidOperationException("Hello, World as error");
    }
}

运行后输出的以下内容同样有上述疑问:

Unhandled exception. System.InvalidOperationException: Hello, World as error
at ConsoleApp1.Program.Main(String[] args) in C:\Users\Admin\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 8

同时想知道:是否存在无需使用try catch、无需在所有可能出错处手动记录日志的处理方式?

解答

一、未处理异常输出的来源

这些格式不同的错误信息是**.NET运行时的默认未处理异常处理机制**生成的。当应用中出现未被捕获的异常时,.NET运行时会直接将异常详情输出到标准错误流(stderr),而Docker会收集容器的stderr和stdout内容作为日志,所以你能在Docker日志中看到它。

这种机制和你配置的Serilog无关,它是.NET运行时的底层行为,目的是在应用崩溃前提供最基础的错误诊断信息。

二、重定向这类消息的方案

可以通过以下方式将这类未处理异常纳入Serilog的日志体系,从而实现将其写入数据库、发送HTTP请求等需求:

1. 配置全局未处理异常捕获(针对Web应用)

在.NET 6+的Web应用中,Program.cs里可以通过注册全局异常处理中间件,结合Serilog记录异常:

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

// 其他服务配置...

var app = builder.Build();

// 注册全局异常处理中间件
app.Use(async (context, next) =>
{
    try
    {
        await next();
    }
    catch (Exception ex)
    {
        // 使用Serilog记录异常
        var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
        logger.LogCritical(ex, "未处理的全局异常");
        
        // 自定义响应逻辑(可选)
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        await context.Response.WriteAsync("服务器内部错误");
    }
});

// 其他中间件配置...
app.Run();

2. 捕获应用域级别的未处理异常(控制台/Web应用通用)

通过订阅AppDomain.CurrentDomain.UnhandledException事件,将未处理异常交给Serilog处理:

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

// 订阅应用域未处理异常事件
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
    var exception = e.ExceptionObject as Exception;
    if (exception != null)
    {
        Log.Fatal(exception, "应用域未处理异常,应用即将终止");
    }
};

// 控制台应用可直接在Main方法开头添加这段代码

3. 处理异步代码的未观察任务异常

如果存在异步代码中未处理的任务异常,还需要订阅TaskScheduler.UnobservedTaskException事件:

TaskScheduler.UnobservedTaskException += (sender, e) =>
{
    Log.Fatal(e.Exception, "未观察到的任务异常");
    e.SetObserved(); // 标记异常已处理,避免应用崩溃
};

三、无需手动try/catch的全局处理方式

上面提到的全局异常中间件(Web应用)和应用域事件订阅(通用)就是无需在每个可能出错的地方写try/catch的方案:

  • Web应用:全局异常中间件会捕获所有请求处理流程中未被捕获的异常
  • 控制台/Web通用:AppDomain.CurrentDomain.UnhandledException会捕获应用域内几乎所有未被捕获的异常,包括主线程和后台线程的异常

通过这些方式,所有未处理异常都会被Serilog统一记录,你可以通过Serilog的Sink组件(比如Serilog.Sinks.MSSqlServer、Serilog.Sinks.Http)将日志写入数据库或发送HTTP请求到其他服务。


内容的提问来源于stack exchange,提问作者Patrick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 23:54:42