我正在用ASP.NET Core 5实现一个API,并且我已经使用Attribute添加了响应缓存。现在,为了调试和跟踪目的,我想将日志记录添加到该属性中。
所以我的代码看起来像这个
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CachedAttribute : Attribute, IAsyncActionFilter
{
private readonly int _timeToLiveSeconds;
public CachedAttribute(int timeToLiveSeconds)
{
_timeToLiveSeconds = timeToLiveSeconds;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
//Get cache setting, check if enabled
//get cache & logger services
var cacheService = context.HttpContext.RequestServices.GetRequiredService<IResponseCacheService>();
var loggerService = context.HttpContext.RequestServices.GetRequiredService<ILogger<ProductController>>();
loggerService.LogDebug("Debug message");
//Do work here
loggerService.LogTrace("tracing message");
//Do more work here
}
}
这很好用,没问题。
现在我希望在多个控制器中重用这个属性。所以我需要把这条线改成更通用的
var loggerService = context.HttpContext.RequestServices.GetRequiredService<ILogger<ProductController>>();
所以我试着从ProductController
改为ControllerBase
,
var loggerService = context.HttpContext.RequestServices.GetRequiredService<ILogger<ControllerBase>>();
但这行不通!我没有收到任何错误,但也没有生成日志记录。
那么,如何使这种日志记录更通用,以便与不同的控制器一起工作呢?
如果有什么不同的话,我正在使用SeriLog。
根据Ihusan Ahmed的评论,我能够按照以下解决问题
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var loggerService = loggerFactory.CreateLogger(context.Controller.GetType());
然后将其用作正常
loggerService.LogDebug("Debug message");
//Do work here
loggerService.LogTrace("tracing message");
不是一个具体的解决方案,而是一个粗略的草图:
问题是,您需要知道应用了属性的类型才能使用正确的泛型参数。
不完全确定,但我认为你应该从context.Controller.GetType()
获得这些信息。
这样,就可以使用MakeGenericType()
通过反射来形成方法调用。返回值在第一步object
中,但您应该能够将其强制转换为ILogger
,并根据需要使用方法。
ILogger<T>
中的T
只是对记录器实例生成的日志消息进行分类的一种方法。它没有其他意义,也没有什么可以阻止您在类Bar
中使用ILogger<Foo>
。
在您的案例中,您已经在CachedAttribute
中使用了ILogger<ProductController>
。这意味着在属性内生成的日志消息现在被标记为属于ProductController
。根据您的设置,这可能有意义,也可能没有意义。
通常,您会看到记录器总是被检索到属于您正在使用它的类。因此,在您的情况下,检索ILogger<CachedAttribute>
可能更有意义。这样,就可以很容易地识别缓存机制产生的日志消息,而不是控制器或其他东西产生的消息。
现在,回到您所观察到的问题。如上所述,没有什么可以阻止您在属性中使用ILogger<ControllerBase>
。那么这里发生了什么?这里的问题是ControllerBase
属于名称空间Microsoft.AspNetCore.Mvc
。
ASP.NET Core日志记录系统中的类别有一个有趣的地方:它允许按类别全名(包括名称空间(过滤日志消息。因此,当您使用ILogger<ProductController>
记录消息时,分配的类别可能是MyApp.Controllers.ProductController
,但对于ILogger<ControllerBase>
,类别现在是Microsoft.AspNetCore.Mvc.ControllerBase
。ASP.NET Core中的默认配置将"Microsoft"
类别配置为仅显示警告:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
因此,如果您使用的是ILogger<ControllerBase>
记录器,那么您现在处于"Microsoft"
日志记录类别领域,这意味着日志记录配置可能会影响您在输出中看到的内容。但是,如果您使用ILogger<ProductController>
或ILogger<CachedAttribute>
,那么您就在"MyApp"
内部,因此将应用默认的日志级别过滤器。