如何避免Microsoft.Extensions.Logging中框架与业务代码重复记录错误?
避免框架与业务代码重复记录错误及区分重试虚假错误的解决方案
一、避免框架与业务代码重复记录同一错误
方案1:精准控制框架命名空间的日志级别
通过配置关闭框架中负责记录异常的特定命名空间日志,只保留业务代码的日志记录。针对ASP.NET Core、EF Core等常见框架,可做如下配置:
"Logging": { "LogLevel": { "Default": "Warning", "MyNamespace": "Warning", // 关闭ASP.NET Core异常处理中间件的日志 "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None", // 关闭EF Core的异常日志(若业务代码会统一处理EF抛出的异常) "Microsoft.EntityFrameworkCore": "None" } }
注意:需根据实际使用的框架组件调整命名空间,比如EF Core的更新操作日志对应Microsoft.EntityFrameworkCore.Update,可单独针对其调整级别。
方案2:标记已处理异常并过滤日志
在业务代码处理异常时给异常添加标记,通过日志过滤器拦截框架端的重复日志:
- 配置日志过滤器(Program.cs中):
builder.Services.Configure<LoggerFilterOptions>(options => { options.AddFilter((category, level, state) => { // 提取日志中的异常对象 if (state is IEnumerable<KeyValuePair<string, object>> props) { var exception = props.FirstOrDefault(p => p.Key == "Exception").Value as Exception; // 过滤已被业务标记为处理过的异常日志 if (exception?.Data.Contains("IsHandledByBusiness") == true) { return false; } } return true; }); });
- 业务代码中标记异常:
try { // 业务操作逻辑 } catch (Exception ex) { // 标记该异常已被业务代码处理 ex.Data["IsHandledByBusiness"] = true; _logger.LogError(ex, "业务操作失败:{OperationDesc}", "创建订单"); throw; // 若需上层框架处理(如返回错误响应)则保留,否则可移除 }
方案3:全局统一异常处理
自定义全局异常处理中间件,统一捕获所有异常并记录一次,同时关闭框架自带的异常日志:
- 实现自定义中间件:
public class GlobalExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILogger<GlobalExceptionMiddleware> _logger; public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception ex) { _logger.LogError(ex, "全局捕获异常:{Path}", context.Request.Path); // 处理响应逻辑,如返回JSON错误信息 context.Response.StatusCode = StatusCodes.Status500InternalServerError; await context.Response.WriteAsJsonAsync(new { Message = "服务器内部错误" }); } } }
- 注册中间件(Program.cs中):
app.UseMiddleware<GlobalExceptionMiddleware>(); // 移除框架自带的ExceptionHandlerMiddleware(如果已添加) // app.UseExceptionHandler("/Error");
同时在配置中关闭框架相关异常日志,确保只有全局中间件记录一次。
二、区分第三方框架重试时的虚假错误与真正错误
方案1:按日志级别过滤重试过程日志
多数框架(如EF Core、Polly)在重试时记录的是Debug或Warning级别日志,最终失败才会记录Error级别。通过配置只保留Error级别日志,过滤重试过程中的日志:
"Logging": { "LogLevel": { "Microsoft.EntityFrameworkCore.Update": "Error", // EF更新操作仅记录最终失败 "Polly": "Error" // Polly重试仅记录最终失败 } }
方案2:自定义重试策略的日志逻辑
使用Polly等重试库时,手动控制日志记录,仅在最终失败时记录Error,重试过程记录低级别日志(生产环境可关闭):
var retryPolicy = Policy .Handle<DbUpdateException>() // 针对EF瞬时异常 .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, retryCount, context) => { // 重试过程仅记录Debug级别,生产环境可关闭Debug日志 _logger.LogDebug(exception, "重试第{RetryCount}次,等待{WaitTime}秒", retryCount, timeSpan.TotalSeconds); }); // 执行业务操作 await retryPolicy.ExecuteAsync(async () => { await _dbContext.SaveChangesAsync(); });
方案3:结构化日志添加标记
在日志中添加自定义字段(如IsRetryAttempt、FinalError),通过日志分析工具筛选:
- 若框架支持,通过日志增强添加标记;
- 业务代码记录最终错误时添加
FinalError: true,运维可通过该字段筛选真正需要关注的错误。
内容的提问来源于stack exchange,提问作者Stig
相关产品推荐
相关产品推荐

