这是Serilog系列的第三篇文章。html
做者:依乐祝mvc
在我上篇文章中,我描述了如何配置Serilog的RequestLogging中间件以向Serilog的请求日志摘要中添加其余属性(例如请求主机名或选定的端点名称)。这些属性都在HttpContext
中可用,所以能够由中间件自己直接添加。ui
其余属性,例如MVC特定的功能,像操做方法ID,RazorPages处理程序名称或ModelValidationState,仅在MVC上下文中可用,所以Serilog的中间件不能直接访问。.net
在本文中,我将展现如何建立action/page
过滤器来为您记录这些属性,以便中间件能够在后续建立日志时访问。日志
Serilog的建立者Nicholas Blumhardt以前已经解决了这个话题。解决方案很是类似,尽管他在他的示例中建立了一个特性,您可使用该特性来装饰actions/controllers。我在本文中跳过了这种方法,并要求将其全局应用,我但愿这将是常见的解决方案。code
就目前而言,ASP.NET Core中的一个特征是许多行为被MVC“基础结构”锁定在了MVC框架内部来实现。端点路由是采用MVC功能并将其下移到核心框架中的首要工做之一。ASP.NET Core团队一直在努力将更多MVC特定功能(例如模型绑定或操做结果)从MVC中移除,而后“下推”到核心框架中。有关此内容的更多信息,请参见Ryan Nowak在NDC上对Houdini项目的讨论。htm
可是,就目前状况而言,MVC内仍然存在一些不容易从应用程序其余部分访问的特性。当咱们考虑到咱们的Serilog的请求记录中间件的时候,这意味着有些属性咱们也是不容易记录的。例如:中间件
OnGet
)1fbc88fa-42db-424f-b32b-c2d0994463f1
)MyController.SomeApiMethod (MyTestApp)
){action = "SomeApiMethod", controller = "My", page = ""}
)True
/ False
)在上一篇文章中我展现了如何使用RequestLogging中间件的扩展方法经过使用IDiagnosticContext
将附加属性写入Serilog的请求日志中。这也仅适用于在HttpContext
可用的值。在这篇文章中,我将展现如何在过滤器中使用IDiagnosticContext
,以及将MVC特定值添加到日志中。我还将展现如何在page过滤器中添加RazorPages特定的值(如HandlerName
)。
过滤器至关于为每一个请求运行的相似于MVC的微型中间件管道。.NET Core MVC中有多种类型的过滤器,每种类型的过滤器在MVC过滤器管道中的有着不一样的用途(有关更多详细信息,请参见此文章)。在本文中,咱们将使用最多见的过滤器之一,即Action过滤器。
Action过滤器在执行MVC操做方法以前和以后运行。他们能够访问许多MVC属性的值,例如正在执行的Action及其将被调用的参数。
下面的Action过滤器直接实现IActionFilter
。该OnActionExecuting
方法在调用action方法以前被调用,并将额外的MVC特定属性添加到经过构造函数传入的IDiagnosticContext
中。
public class SerilogLoggingActionFilter : IActionFilter { private readonly IDiagnosticContext _diagnosticContext; public SerilogLoggingActionFilter(IDiagnosticContext diagnosticContext) { _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); } public void OnActionExecuting(ActionExecutingContext context) { _diagnosticContext.Set("RouteData", context.ActionDescriptor.RouteValues); _diagnosticContext.Set("ActionName", context.ActionDescriptor.DisplayName); _diagnosticContext.Set("ActionId", context.ActionDescriptor.Id); _diagnosticContext.Set("ValidationState", context.ModelState.IsValid); } // Required by the interface public void OnActionExecuted(ActionExecutedContext context) { } }
在将MVC服务添加到应用程序中时,能够在如下位置全局注册过滤器Startup.ConfigureServices()
:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(opts => { opts.Filters.Add<SerilogLoggingPageFilter>(); }); // ... other service registration }
不管你使用
AddControllers
,AddControllersWithViews
,AddMvc
,或AddMvcCore
的方式你均可以采用一样的方式来添加全局过滤器。
有了这个配置以后,若是你调用一个MVC控制器,你在Serilog的请求日志消息中会看到额外的数据(ActionName
,ActionId
,和RouteData
,ValidationState
)记录:
您能够在此处将所需的任何其余数据添加到日志中。只需注意记录参数值-切记不要记录敏感或我的身份信息!
Nicholas Blumhardt在他的帖子中建议的Action过滤器是从
ActionFilterAttribute
派生的,所以能够将其直接用做控制器和Action的特性。不幸的是,这意味着您必须使用服务定位来从每一个请求的HttpContext
中检索单例的IDiagnosticContext
。个人方法能够改用构造函数注入,可是不建议将其用做属性,所以必须如上所述全局使用。并且,MVC将在个人实现中使用做用域生存期,而不是单例,所以它会在每一个请求中建立一个新实例。
若是要记录其余集中MVC过滤器中的值,则能够以相同的方式实现其余过滤器,例如资源过滤器,结果过滤器或受权过滤器。
上面实现的IActionFilter
过滤器在MVC和API控制器上可以正常运行,但它不会对RazorPages起做用。若是要为选择的给定Razor页面记录HandlerName,则须要建立一个自定义的IPageFilter
。
页面过滤器直接相似于Action过滤器,但它们仅适用于Razor页面。如下示例从PageHandlerSelectedContext
中检索处理程序名称并将其记录为属性RazorPageHandler
。在这种状况下,还须要一些样板代码,但过滤器的功能仍是很是基础的-调用IDiagnosticContext.Set()
以记录属性。
public class SerilogLoggingPageFilter : IPageFilter { private readonly IDiagnosticContext _diagnosticContext; public SerilogLoggingPageFilter(IDiagnosticContext diagnosticContext) { _diagnosticContext = diagnosticContext ?? throw new ArgumentNullException(nameof(diagnosticContext)); } //Required by the interface public void OnPageHandlerExecuted(PageHandlerExecutedContext context) { } public void OnPageHandlerExecuting(PageHandlerExecutingContext context) { } public void OnPageHandlerSelected(PageHandlerSelectedContext context) { var name = context.HandlerMethod?.Name ?? context.HandlerMethod?.MethodInfo.Name; if (name != null) { _diagnosticContext.Set("RazorPageHandler", name); } } }
请注意,咱们以前编写的IActionFilter
代码不会在Razor Pages上运行,所以,若是您也想记录RazorPages RouteData
或ValidationState
RazorPages的其余详细信息,则也须要在此处添加它。该context
属性包含您可能须要的大多数属性,例如ModelState
和ActionDescriptor
。
接下来,您须要在Startup.ConfigureServices()
方法中注册页面过滤器:
public void ConfigureServices(IServiceCollection services) { //services.AddMvcCore( // opts => opts.Filters.Add<SerilogLoggingPageFilter>() // ); services.AddRazorPages().AddMvcOptions( opts => opts.Filters.Add<SerilogLoggingPageFilter>() ) ; }
添加过滤器后,对“Razor页面”的请求如今能够看到添加的附加属性,IDiagnosticContext
这些属性将添加到Serilog请求日志中。请参见下图中的RazorPageHandler
属性:
默认状况下,当用Serilog的请求日志记录中间件替换ASP.NET Core基础结构中的日志记录时,您会丢失一些信息(与开发环境的默认配置相比)。在本文中,我将展现如何自定义Serilog,RequestLoggingOptions
以从新添加特定于MVC的其余属性。
要将与MVC相关的属性添加到Serilog请求日志中,请建立一个IActionFilter
并使用IDiagnosticContext.Set()
来添加属性。要将与Razor页面相关的属性添加到Serilog请求日志中,请在IPageFilter
中使用IDiagnosticContext
的相同方法建立和添加属性。
下一节让咱们一块儿探讨下如何从Serilog请求记录中排除运行情况检查端点。