专栏首页依乐祝如何使用Serilog.AspNetCore记录ASP.NET Core3.0的MVC属性

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

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

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

作者:依乐祝 译文地址:https://www.cnblogs.com/yilezhu/p/12243984.html 原文地址:https://andrewlock.net/using-serilog-aspnetcore-in-asp-net-core-3-logging-mvc-propertis-with-serilog/

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

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

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

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

记录来自MVC的其他信息

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

但是,就目前情况而言,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)记录:

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

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的请求日志记录中间件替换ASP.NET Core基础结构中的日志记录时,您会丢失一些信息(与开发环境的默认配置相比)。在本文中,我将展示如何自定义Serilog,RequestLoggingOptions以重新添加特定于MVC的其他属性。

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

下一节让我们一起探讨下如何从Serilog请求记录中排除运行状况检查端点。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Serilog高级玩法之用Serilog记录所选终结点附加属性

    这是该系列的第二篇文章:在ASP.NET Core 3.0中使用Serilog.AspNetCore。

    依乐祝
  • 如何利用Serilog的RequestLogging来精简ASP.NET Core的日志输出

    这是该系列的第一篇文章:在ASP.NET Core 3.0中使用Serilog.AspNetCore。

    依乐祝
  • 如何从Serilog请求日志记录中排除健康检查终结点

    这是在ASP.NET Core 3.X中使用Serilog.AspNetCore系列文章的第四篇文章:。

    依乐祝
  • Ken的杂谈从ASP.NET Core 2.1迁移到3.1

    Ken的杂谈从2017年就开始采用 .NET Core作为开发框架,当时用的还是 .NET Core 2.0。 随后,.NET Core 2.1(LTS) 在2...

    KenTalk
  • .NET Core 学习资料精选:入门

    本系列文章,主要分享一些.NET Core比较优秀的社区资料和微软官方资料。我进行了知识点归类,让大家可以更清晰的学习.NET Core。

    梁规晓
  • ASP.NET Core 3.0 上的gRPC服务模板初体验(多图)

    早就听说ASP.NET Core 3.0中引入了gRPC的服务模板,正好趁着家里电脑刚做了新系统,然后装了VS2019的功夫来体验一把。同时记录体验的过程。如果...

    依乐祝
  • 从壹开始学习NetCore 44 ║ 最全的 netcore 3.0 升级实战方案

    言归正传,? 从2018年8月就开始听说 netcore 要准备3.0了,? 到了近期 v3.0.0-preview9 的发布(截止目前,3.0RC1已经发布...

    梁规晓
  • 从ASP.NET Core2.2到3.0你可能会遇到这些问题

    趁着假期的时间所以想重新学习下微软的官方文档来巩固下基础知识。我们都知道微软目前已经发布了.NET Core3.0的第三个预览版,同时我家里的电脑也安装了vs2...

    依乐祝
  • ASP.NET Core端点路由 作用原理

    端点路由(Endpoint Routing)最早出现在ASP.NET Core2.2,在ASP.NET Core3.0提升为一等公民。

    小码甲

扫码关注云+社区

领取腾讯云代金券