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

如何避免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:标记已处理异常并过滤日志

在业务代码处理异常时给异常添加标记,通过日志过滤器拦截框架端的重复日志:

  1. 配置日志过滤器(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;
    });
});
  1. 业务代码中标记异常:
try
{
    // 业务操作逻辑
}
catch (Exception ex)
{
    // 标记该异常已被业务代码处理
    ex.Data["IsHandledByBusiness"] = true;
    _logger.LogError(ex, "业务操作失败:{OperationDesc}", "创建订单");
    throw; // 若需上层框架处理(如返回错误响应)则保留,否则可移除
}

方案3:全局统一异常处理

自定义全局异常处理中间件,统一捕获所有异常并记录一次,同时关闭框架自带的异常日志:

  1. 实现自定义中间件:
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 = "服务器内部错误" });
        }
    }
}
  1. 注册中间件(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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:28:14