单个错误触发三条日志记录问题(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
相关产品推荐
相关产品推荐

