译者: Akini Xu
原文: Logging MVC properties with Serilog.AspNetCore
作者: Andrew Lock
此文是在ASP.NET Core 3.0中使用Serilog第3篇:
在本系列的上一篇中,我介绍了如何配置Serilog的RequestLogging
中间件,来向Serilog的摘要日志中添加额外属性。 这些属性可以从HttpContext
中获得,直接被中间件使用。
有一些属性(MVC特有的功能,例如Action名称,RazorPages处理程序名称或ModelValidationState等)仅在MVC上下文中可用,因此Serilog的中间件不能直接访问。
在本文中,我将介绍如何通过创建Action/Page的过滤器来记录这些属性,以方便中间件生成日志时使用。
Serilog的作者Nicholas Blumhardt在一篇文章中解决了这个问题。 他在示例中创建了一个attribute,并在actions/controllers中使用。 本文并没有采用这种方式,我想实现一种更通用的解决方案。 另外还可以查看他有关ASP.NET Core 3.0的Serilog相关文章
记录MVC中的额外信息
ASP.NET Core中有很多功能被封装在MVC“基础架构”之内。 目前,端点路由这个功能已经被迁移到.NET Core框架中实现了。 ASP.NET Core团队一直在努力将更多的MVC特有功能(例如模型绑定或操作结果)从MVC内部移出,然后迁移到核心框架中。 有关更多信息,请参见Ryan Nowak在NDC上对Houdini项目的讨论。
就目前情况而言,MVC内部仍然有一些信息,是不太容易从外部获取的。 当使用Serilog的RequestLogging
中间件时,以下信息就不太容易获取:
- HandlerName (
OnGet
) - ActionId (
1fbc88fa-42db-424f-b32b-c2d0994463f1
) - ActionName (
MyController.SomeApiMethod (MyTestApp)
) - RouteData (
{action = "SomeApiMethod", controller = "My", page = ""}
) - ValidationState (
True
/False
)
在上一篇文章中,我介绍了如何使用RequestLogging
中间件将额外附加信息写入Serilog的请求日志。 这仅适用于HttpContext
中可访问的属性。 在这篇文章中,我将介绍如何在action filter中使用IDiagnosticContext
将MVC特有的属性也添加到日志中。 还将介绍如何使用page filter添加RazorPages特有的属性(例如HandlerName)。
使用Action过滤器记录MVC特有属性
过滤器相当于MVC框架中的一个微型的中间件管道。MVC中有多种类型的过滤器,它们会在过滤器管道的不同位置运行(有关MVC过滤器更多详细信息,请参见此文章)。 在本文中,我们将使用最常见的过滤器之一,即Action filter。
Action过滤器会在MVCAction方法执行前和执行后运行。 这个过滤器可以访问到许多MVC特有的属性,例如将要执行的Action及执行它的参数。
下面的Action过滤器实现了IActionFilter
接口。 当Action的方法被执行之前,会先调用此过滤器的OnActionExecuting
方法,在此方法中获取MVC特有的属性,添加到IDiagnosticContext
中。
public class SerilogLoggingActionFilter : IActionFilter |
在Startup.ConfigureServices()
中注入MVC服务时,同时注入全局范围过滤器:
public void ConfigureServices(IServiceCollection services) |
无论您使用的是
AddControllers
,AddControllersWithViews
,AddMvc
还是AddMvcCore
,都可以以相同的方式注入全局过滤器。
完成此配置后,再次访问MVC的Controller,则会在Serilog请求日志消息中看到额外信息(ActionName
,ActionId
和RouteData
,ValidationState
):
您可以使用此方式,添加您所需的任何其他信息添加到日志中。 注意:不要记录敏感信息或个人身份信息!
Nicholas Blumhardt的文章中建议,Action过滤器从
ActionFilterAttribute
派生的,方便其用作于controller和action。这样做意味着,必须使用IServiceProvider
来获取单例的IDiagnosticContext对象。上面的方法改为使用构造函数注入,因此不能当Attribute使用。 另外,生命周期是scoped,非singleton,因此每次请求都会创建一个新实例。
如果要记录MVC过滤器管道中其他位置的相关属性,可以采用类似的方式实现其它过滤器,例如Resource filter、Result filter、Authorization filter。
使用Page过滤器记录RazorPages属性
上面的IActionFilter
只能在MVC controller和API controller上运行,但不能在RazorPages
上运行。 如果要记录为指定Razor页面对应的HandlerName
,则需要创建一个自定义IPageFilter
。
Page filters类似于Action filters,但只仅适用于Razor Pages 。 下面的示例从PageHandlerSelectedContext
获取HandlerName
。最后调用IDiagnosticContext .Set()
来记录属性。
public class SerilogLoggingPageFilter : IPageFilter |
请注意,之前编写的IActionFilter
不会在Razor Pages上运行,因此,如果也想为RazorPages记录诸如RouteData
或ValidationState
之类的其他详细信息,那么也需要在此处添加那些代码。 context
属性包含大多数需要的属性,例如ModelState
和ActionDescriptor
。
接下来,您需要在Startup.ConfigureServices()
方法中注册Page filters:
public void ConfigureServices(IServiceCollection services) |
添加过滤器后,对Razor Pages的请求额外属性将会添加到Serilog请求日志中。 请参见下图中的RazorPageHandler
属性:
总结
默认情况下,当用Serilog的请求日志记录中间件替换ASP.NET Core基础日志组件时,会丢失一些信息(与开发环境的默认配置相比)。 在本文中,我介绍了如何自定义Serilog的RequestLoggingOptions
来添加MVC特有的其他属性。
需要添加MVC相关的属性到Serilog请求日志中,创建IActionFilter
并使用IDiagnosticContext.Set()
添加属性。
需要添加Razor页面相关的属性添加到Serilog请求日志中,创建IPageFilter
并使用IDiagnosticContext
添加属性。