.NET 8中IExceptionHandler内Serilog无法写入SQL Server数据库问题
解决.NET Core 8全局异常处理器中Serilog无法写入SQL Server的问题
问题场景
已配置Serilog将日志写入文件、控制台和SQL Server数据库,在应用其他区域及ExceptionHandlingMiddleware构造函数中,日志均可正常写入数据库,但在TryHandleAsync方法中,仅文件和控制台日志生效,数据库写入失败。相关代码如下:
异常处理器代码
public class ExceptionHandlingMiddleware : IExceptionHandler { private readonly ILogger<ExceptionHandlingMiddleware> _logger; public ExceptionHandlingMiddleware(ILogger<ExceptionHandlingMiddleware> logger) { _logger = logger; // 可写入文件、控制台及MSSQL数据库 _logger.LogInformation("1-ExceptionHandlingMiddleware"); } public ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { // 仅文件和控制台日志生效,数据库写入失败 _logger.LogInformation("2-ExceptionHandlingMiddleware"); return ValueTask.FromResult(false); } }
Program.cs配置
builder.Services.AddExceptionHandler<ExceptionHandlingMiddleware>(); app.UseExceptionHandler();
核心原因
当请求发生异常进入IExceptionHandler的TryHandleAsync方法时,当前请求的Scoped服务范围(Scope)已经开始销毁:
- 如果Serilog的SQL Sink依赖Scoped的
DbContext,此时该上下文已被释放,无法执行数据库写入 - 若请求存在未提交的事务,异常会触发事务回滚,若日志写入被包含在该事务中,也会被回滚
HttpContext的状态已进入请求收尾阶段,部分依赖请求上下文的服务已不可用
解决方案
方案1:配置Serilog SQL Sink使用独立连接(不依赖Scoped DbContext)
确保Serilog直接使用连接字符串初始化SQL Sink,而非依赖注入的DbContext,这样日志写入不依赖请求Scope。
修改Serilog配置示例:
Log.Logger = new LoggerConfiguration() .WriteTo.Console() .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day) .WriteTo.MSSqlServer( connectionString: "Your_SQL_Server_Connection_String", sinkOptions: new MSSqlServerSinkOptions { TableName = "Logs" }) .CreateLogger();
方案2:在TryHandleAsync中创建新的服务Scope
手动创建独立的Scope,在新Scope内执行日志写入,避免依赖已销毁的请求Scope:
修改ExceptionHandlingMiddleware代码:
public class ExceptionHandlingMiddleware : IExceptionHandler { private readonly ILogger<ExceptionHandlingMiddleware> _logger; private readonly IServiceProvider _serviceProvider; public ExceptionHandlingMiddleware(ILogger<ExceptionHandlingMiddleware> logger, IServiceProvider serviceProvider) { _logger = logger; _serviceProvider = serviceProvider; _logger.LogInformation("1-ExceptionHandlingMiddleware"); } public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { // 创建新的服务Scope using var scope = _serviceProvider.CreateScope(); var scopedLogger = scope.ServiceProvider.GetRequiredService<ILogger<ExceptionHandlingMiddleware>>(); // 使用新Scope的Logger写入日志 scopedLogger.LogInformation("2-ExceptionHandlingMiddleware"); return false; } }
方案3:确保日志操作脱离请求事务
如果日志写入被包含在请求事务中,异常回滚会导致日志丢失。可以:
- 配置Serilog SQL Sink使用独立事务,不参与请求的事务
- 手动开启新的数据库连接执行日志写入,避免复用请求的连接
验证步骤
- 修改配置或代码后,触发异常进入
TryHandleAsync - 检查SQL Server的日志表,确认日志已正常写入
- 同时验证文件和控制台日志仍正常输出
内容的提问来源于stack exchange,提问作者Jens B
相关产品推荐
相关产品推荐

