如何使用Serilog.AspNetCore记录ASP.NET Core3.0的MVC属性

这是Serilog系列的第三篇文章。html

  1. 第1部分-使用Serilog RequestLogging减小日志详细程度
  2. 第2部分-使用Serilog记录所选的终结点属性
  3. 第3部分-使用Serilog.AspNetCore记录MVC属性(本文)
  4. 第4部分-从Serilog请求记录中排除运行情况检查端点

做者:依乐祝mvc

译文地址:http://www.javashuo.com/article/p-udjlbhyf-ka.html框架

原文地址:https://andrewlock.net/using-serilog-aspnetcore-in-asp-net-core-3-logging-mvc-propertis-with-serilog/函数

在我上篇文章中,我描述了如何配置Serilog的RequestLogging中间件以向Serilog的请求日志摘要中添加其余属性(例如请求主机名或选定的端点名称)。这些属性都在HttpContext中可用,所以能够由中间件自己直接添加。ui

其余属性,例如MVC特定的功能,像操做方法ID,RazorPages处理程序名称或ModelValidationState,在MVC上下文中可用,所以Serilog的中间件不能直接访问。.net

在本文中,我将展现如何建立action/page过滤器来为您记录这些属性,以便中间件能够在后续建立日志时访问。日志

Serilog的建立者Nicholas Blumhardt以前已经解决了这个话题。解决方案很是类似,尽管他在他的示例中建立了一个特性,您可使用该特性来装饰actions/controllers。我在本文中跳过了这种方法,并要求将其全局应用,我但愿这将是常见的解决方案。code

记录来自MVC的其余信息

就目前而言,ASP.NET Core中的一个特征是许多行为被MVC“基础结构”锁定在了MVC框架内部来实现。端点路由是采用MVC功能并将其下移到核心框架中的首要工做之一。ASP.NET Core团队一直在努力将更多MVC特定功能(例如模型绑定或操做结果)从MVC中移除,而后“下推”到核心框架中。有关此内容的更多信息,请参见Ryan Nowak在NDC上对Houdini项目的讨论htm

可是,就目前状况而言,MVC内仍然存在一些不容易从应用程序其余部分访问的特性。当咱们考虑到咱们的Serilog的请求记录中间件的时候,这意味着有些属性咱们也是不容易记录的。例如:中间件

  • HandlerName(OnGet
  • ActionId(1fbc88fa-42db-424f-b32b-c2d0994463f1
  • ActionName (MyController.SomeApiMethod (MyTestApp)
  • RouteData({action = "SomeApiMethod", controller = "My", page = ""}
  • ValidationState(True/ False

上一篇文章中我展现了如何使用RequestLogging中间件的扩展方法经过使用IDiagnosticContext将附加属性写入Serilog的请求日志中。这也仅适用于在HttpContext可用的值。在这篇文章中,我将展现如何在过滤器中使用IDiagnosticContext,以及将MVC特定值添加到日志中。我还将展现如何在page过滤器中添加RazorPages特定的值(如HandlerName)。

使用自定义过滤器记录MVC属性

过滤器至关于为每一个请求运行的相似于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
}

不管你使用AddControllersAddControllersWithViewsAddMvc,或AddMvcCore的方式你均可以采用一样的方式来添加全局过滤器。

有了这个配置以后,若是你调用一个MVC控制器,你在Serilog的请求日志消息中会看到额外的数据(ActionNameActionId,和RouteDataValidationState)记录:

在Serilog请求日志中记录的其余MVC属性的图像

您能够在此处将所需的任何其余数据添加到日志中。只需注意记录参数值-切记不要记录敏感或我的身份信息!

Nicholas Blumhardt在他的帖子中建议的Action过滤器是从ActionFilterAttribute派生的,所以能够将其直接用做控制器和Action的特性。不幸的是,这意味着您必须使用服务定位来从每一个请求的HttpContext中检索单例的IDiagnosticContext。个人方法能够改用构造函数注入,可是不建议将其用做属性,所以必须如上所述全局使用。并且,MVC将在个人实现中使用做用域生存期,而不是单例,所以它会在每一个请求中建立一个新实例。

若是要记录其余集中MVC过滤器中的值,则能够以相同的方式实现其余过滤器,例如资源过滤器,结果过滤器或受权过滤器。

使用自定义page过滤器记录RazorPages属性

上面实现的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 RouteDataValidationStateRazorPages的其余详细信息,则也须要在此处添加它。该context属性包含您可能须要的大多数属性,例如ModelStateActionDescriptor

接下来,您须要在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请求日志中记录了额外的Razor Page属性的图像

总结

默认状况下,当用Serilog的请求日志记录中间件替换ASP.NET Core基础结构中的日志记录时,您会丢失一些信息(与开发环境的默认配置相比)。在本文中,我将展现如何自定义Serilog,RequestLoggingOptions以从新添加特定于MVC的其余属性。

要将与MVC相关的属性添加到Serilog请求日志中,请建立一个IActionFilter并使用IDiagnosticContext.Set()来添加属性。要将与Razor页面相关的属性添加到Serilog请求日志中,请在IPageFilter中使用IDiagnosticContext的相同方法建立和添加属性。

下一节让咱们一块儿探讨下如何从Serilog请求记录中排除运行情况检查端点。

相关文章
相关标签/搜索