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

ActionFilter中GetCurrentClassLogger日志需构造调用才生效

问题根因

这个现象是典型的日志组件初始化时序+框架生命周期理解偏差导致的,本地正常部署异常是环境启动速度、资源释放时机差异造成的:

  • 你虽然没有用静态Logger字段,但LogManager.GetCurrentClassLogger()本质是直接访问NLog的全局静态日志管理器,NLog默认采用延迟初始化逻辑:第一次调用日志写入方法时,才会加载日志配置、初始化文件/控制台等输出目标、注册日志级别过滤规则。
  • 本地调试时因为调试器附加、热重载、启动速度慢等原因,NLog往往在请求到达前就已经被其他逻辑触发完成初始化,所以不管在哪写日志都正常。
  • 生产部署后应用启动速度快,如果你不在构造函数里提前调用一次日志方法,第一次触发NLog初始化的时机就落在了OnResultExecutionAsync阶段:这个阶段已经处于请求处理的末尾,尤其是如果你的日志写在await next()之后,部分请求级服务、日志缓冲区已经开始进入释放流程,NLog第一次初始化会因为依赖资源不可用直接失败,后续所有日志写入都会被丢弃。
  • 当你在构造函数里提前调用一次_logger.Info(),会在请求处理早期、所有托管资源都正常存活的阶段触发NLog完成全量初始化,后续再写日志就不需要重复初始化,自然能正常输出。

另外你的实现还有一个设计隐患:你让RequestLoggerService继承ActionFilterAttribute,但又通过TypeFilterAttribute创建实例,这种写法会干扰框架对过滤器生命周期的判断,极端场景下会出现实例跨请求复用、状态异常的问题。你希望规避静态Logger、方便单元测试Mock的诉求完全合理,但直接给字段赋值LogManager.GetCurrentClassLogger()的写法本质还是依赖NLog全局静态状态,并没有真正和ASP.NET Core的依赖注入体系对齐。


修复方案

不需要保留构造函数里的预热日志调用,按以下方式改造即可彻底解决问题:

  1. 调整RequestLoggerService的实现,不要继承ActionFilterAttribute,直接实现对应过滤器接口,所有依赖通过构造函数注入,完全交给DI容器管理:
public class RequestLoggerService : IAsyncResultFilter
{
    private readonly bool _logHeader;
    private readonly bool _logBody;
    private readonly ILogger<RequestLoggerService> _logger;

    // 直接注入ILogger,不需要手动调用LogManager获取
    public RequestLoggerService(bool logBody, bool logHeader, ILogger<RequestLoggerService> logger)
    {
        _logHeader = logHeader;
        _logBody = logBody;
        _logger = logger;
    }

    public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
    {
        // 先执行后续请求管道,注意必须加await
        var executedContext = await next();
        
        // 你的日志记录逻辑,注意读取请求体前要先启用HttpRequest缓冲
        // string requestHeadersText = ...;
        // string requestBodyText = ...;
        // _logger.LogInformation("requestHeaders: {Headers}", requestHeadersText);
        // _logger.LogInformation("requestBody: {Body}", requestBodyText);
    }
}
  1. 确认Program.cs中NLog的注册顺序正确,在应用构建早期就完成日志提供程序注册,从根源上避免延迟初始化问题:
var builder = WebApplication.CreateBuilder(args);
// 优先注册日志提供程序
builder.Logging.ClearProviders();
builder.Host.UseNLog();

// 再注册控制器、其他业务服务
builder.Services.AddControllers();
// ...其他服务注册
  1. 你现有的RequestLoggerAttribute不需要做任何修改,TypeFilter会自动从DI容器解析ILogger实例注入,不需要手动传参。单元测试时你可以直接传入Mock的ILogger<RequestLoggerService>实例,完全满足你的测试需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 19:29:00