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

单个错误触发三条日志记录问题(ASP.NET Core+MediatR)

问题分析

单条错误产生3条重复日志的原因:

  • 第一条:ASP.NET Core请求日志中间件记录原请求/api/query/staff的500响应,包含异常栈信息
  • 第二条:ExceptionHandlerMiddleware捕获未处理异常时,框架默认生成的"未处理异常"日志
  • 第三条:请求转发到/error端点后,该端点返回500状态码,请求日志中间件再次记录此请求的响应
解决方案

方案1:通过日志过滤抑制重复日志

在appsettings.json中添加日志过滤规则,关闭ExceptionHandlerMiddleware的默认异常日志,并可选过滤/error端点的请求日志:

"Logging": {
  "LogLevel": {
    "Default": "Information",
    // 关闭ExceptionHandlerMiddleware的未处理异常日志(解决第二条日志)
    "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None",
    // 可选:关闭/error端点的请求日志(解决第三条日志)
    "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": {
      "/error": "None"
    }
  }
}

方案2:自定义异常处理中间件,完全控制日志输出

替代UseExceptionHandler,用自定义中间件捕获异常并仅记录一次日志,避免重复:

1. 创建自定义中间件类

public class CustomExceptionHandlerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<CustomExceptionHandlerMiddleware> _logger;
    private readonly IHostEnvironment _env;

    public CustomExceptionHandlerMiddleware(RequestDelegate next, ILogger<CustomExceptionHandlerMiddleware> logger, IHostEnvironment env)
    {
        _next = next;
        _logger = logger;
        _env = env;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            // 仅记录一次异常日志
            _logger.LogError(ex, "请求处理失败,Path: {Path}", context.Request.Path);

            context.Response.ContentType = "application/json";
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            var errorResponse = _env.IsDevelopment()
                ? new 
                  { 
                      Title = "服务器内部错误", 
                      Message = ex.Message, 
                      StackTrace = ex.StackTrace 
                  }
                : new 
                  { 
                      Title = "服务器内部错误", 
                      Message = "请联系管理员获取帮助" 
                  };

            await context.Response.WriteAsJsonAsync(errorResponse);
        }
    }
}

2. 在Program.cs中注册中间件

注意中间件注册顺序,需放在路由和授权中间件之后,端点映射之前:

var builder = WebApplication.CreateBuilder(args);

// 添加服务...
builder.Services.AddControllers();
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

var app = builder.Build();

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

// 注册自定义异常处理中间件
app.UseMiddleware<CustomExceptionHandlerMiddleware>();

app.MapControllers();

app.Run();

这种方式会彻底替换默认的异常处理流程,仅生成一条包含完整异常信息的日志,同时根据环境返回对应的错误响应。

方案3:调整ErrorsController行为(针对第三条日志)

如果保留UseExceptionHandler,可修改ErrorsController,在开发环境返回200状态码(仅用于避免第三条日志,不推荐,因为状态码不符合HTTP规范):

[Route("/error")]
public IActionResult HandleError([FromServices] IHostEnvironment hostEnvironment)
{
    if (!hostEnvironment.IsDevelopment())
    {
        return NotFound();
    }

    var exception = HttpContext.Features.Get<IExceptionHandlerFeature>().Error!;

    // 返回200状态码,避免请求日志中间件记录500响应
    return Problem(
     detail: exception.StackTrace,
     title: exception.Message,
     statusCode: StatusCodes.Status200OK
    );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 13:15:51