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

如何在.NET Core 3.1应用中借助Serilog SQL Server sink全局记录已处理异常

核心原因

操作过滤器、全局异常中间件仅能捕获向上冒泡到控制器层的未处理异常,仓储层嵌套try/catch逻辑已经将异常内部消化,未向上抛出,因此上层全局捕获组件无法拿到异常信息。

可行解决方案

方案1:仓储层新增异常日志写入逻辑(改造成本最低)

无需修改现有业务流程,仅需在所有catch块内追加日志写入逻辑即可。你之前出现独立类调用日志无法写入数据库的问题,大概率是ILogger依赖注入异常导致,直接使用Serilog提供的静态日志类即可绕开DI问题。
代码示例:

public class EfRepository<T> : IRepository<T>
{
    public async Task YourNestedMethod()
    {
        try
        {
            // 原有业务逻辑
        }
        catch (Exception ex)
        {
            // 保留原有异常处理逻辑,不影响业务正常运行
            HandleOriginalException(ex);
            // 新增日志写入,可自定义附加参数
            Serilog.Log.Error(ex, "仓储层执行异常,方法名:{MethodName}", nameof(YourNestedMethod));
        }
    }
}

如果需要同步写入用户ID、请求路径等上下文信息,可在Serilog配置中开启Enrich.FromLogContext(),并新增中间件统一注入请求上下文:

public class LogContextMiddleware
{
    private readonly RequestDelegate _next;
    public LogContextMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        var userId = context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        using (Serilog.Context.LogContext.PushProperty("UserId", userId))
        using (Serilog.Context.LogContext.PushProperty("RequestPath", context.Request.Path))
        {
            await _next(context);
        }
    }
}

在Startup的Configure方法中将该中间件放在请求管道最前面即可,后续所有位置写入的Serilog日志都会自动携带上下文参数,和你现有操作过滤器的日志格式保持统一。

方案2:AOP动态代理拦截(无侵入改造)

如果不想修改现有仓储层代码,可使用Castle DynamicProxy等AOP框架对所有仓储层方法做动态拦截,自动为方法套入异常捕获逻辑,捕获异常后写入日志再将异常抛出给原有catch块处理,完全不影响现有逻辑。
步骤如下:

  1. 安装Castle.Core Nuget包
  2. 实现拦截器:
public class RepositoryLoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
            // 处理异步方法的异常捕获
            if (invocation.ReturnValue is Task task)
            {
                task.ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        Serilog.Log.Error(t.Exception, "仓储方法{Method}执行异常", invocation.Method.Name);
                    }
                });
            }
        }
        catch (Exception ex)
        {
            Serilog.Log.Error(ex, "仓储方法{Method}执行异常", invocation.Method.Name);
            // 继续抛出异常,交由原有catch块处理
            throw;
        }
    }
}
  1. 在DI注册仓储时用拦截器包装即可,.NET Core 3.1自带DI不支持拦截器,可搭配Autofac等第三方DI框架实现。

方案3:统一异常上报服务(扩展性最强)

如果后续需要对接监控、告警等能力,可单独抽取异常上报服务,所有catch块统一调用该服务做异常处理,便于后续逻辑扩展:

public interface IExceptionReportService
{
    void Report(Exception ex, string moduleName, string methodName);
}

public class SerilogExceptionReportService : IExceptionReportService
{
    public void Report(Exception ex, string moduleName, string methodName)
    {
        // 可扩展告警、监控上报等逻辑
        Serilog.Log.Error(ex, "{Module}模块{Method}方法执行异常", moduleName, methodName);
    }
}

将该服务注册为单例后注入到需要的位置使用即可。

日志无法写入数据库排查思路

如果出现日志未写入数据库的情况,可按顺序排查:

  1. 本地调试时将Serilog同时输出到控制台,确认日志是否正常生成
  2. 检查Serilog配置中SqlServer Sink的连接字符串是否正确,是否开启了即时写入(关闭缓冲区)
  3. 确认数据库账号有对应表的写入权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 05:45:02