如何使用Serilog.AspNetCore记录ASP.NET Core3.0的MVC属性
这是在ASP.NET Core 3.X中使用Serilog.AspNetCore系列文章的第三篇文章:。
- 第1部分-使用Serilog RequestLogging减少日志详细程度
- 第2部分-使用Serilog记录所选的终结点属性
- 第3部分-使用Serilog.AspNetCore记录MVC属性(本文)
- 第4部分-从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
}
无论你使用
AddControllers,AddControllersWithViews,AddMvc,或AddMvcCore的方式你都可以采用同样的方式来添加全局过滤器。
有了这个配置之后,如果你调用一个MVC控制器,你在Serilog的请求日志消息中会看到额外的数据(ActionName,ActionId,和RouteData,ValidationState)记录:

您可以在此处将所需的任何其他数据添加到日志中。只需注意记录参数值-切记不要记录敏感或个人身份信息!
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 RouteData或ValidationStateRazorPages的其他详细信息,则也需要在此处添加它。该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请求记录中排除运行状况检查端点。
如何使用Serilog.AspNetCore记录ASP.NET Core3.0的MVC属性的更多相关文章
- 问题:ClientIDMode属性;结果:ASP.NET 4.0的ClientIDMode属性
ASP.NET 4.0的ClientIDMode属性 时光流逝,我们心爱的ASP.NET也步入了4.0的时代,微软在ASP.NET 4.0中对很多特性做了修改.比如我将要讨论的控件ID机制就是其中之一 ...
- 2019年第一天——使用Visual Studio 2019 Preview创建第一个ASP.Net Core3.0的App
一.前言: 全文翻译自:https://www.talkingdotnet.com/creating-first-asp-net-core-3-0-app-visual-studio-2019/ Vi ...
- asp.net core3.0 mvc 用 autofac
好久没有写文章了,最近在用.net core3.0,一些开发中问题顺便记录: 1.首先nuget引入 Autofac Autofac.Extensions.DependencyInjection 2. ...
- ASP.NET 4.0的ClientIDMode属性
时光流逝,我们心爱的ASP.NET也步入了4.0的时代,微软在ASP.NET 4.0中对很多特性做了修改.比如我将要讨论的控件ID机制就是其中之一. 在ASP.NET 4.0之前我们总是要为控件的Cl ...
- ASP.NET Core3.0 中的运行时编译
运行时编译 通过 Razor 文件的运行时编译补充生成时编译. 当 .cshtml 文件的内容发生更改时,ASP.NET Core MVC 将重新编译 Razor 文件 . 通过 Razor 文件的运 ...
- Postman 调试请求Asp.Net Core3.0 WebApi几种常见的Get/Post/Put/Delete请求
这里就直接截图了,如下(很简单的操作): 1:Get几种请求 2:Post 3:Put 4:Delete 最后,虽然简单,代码还是给放一下(这里只是抛砖引玉的作用,自己可以根据自身的业务需要来做进一 ...
- Asp.net Core3.0 跨域配置
原文:http://www.zilaohu.cn/Jie/Detail_Jie?ID=78840a04-55b8-4988-80b2-f964fd822d63 下面配置后:被拒绝的域请求后,可以进入方 ...
- 探索Asp net core3中的 项目文件、Program.cs和通用host(译)
引言 原文地址 在这篇博客中我将探索一些关于Asp.net core 3.0应用的基础功能--.csproj 项目文件和Program源文件.我将会描述他们从asp.net core 2.X在默认模版 ...
- ASP.NETCore 3.0 Autofac替换及控制器属性注入及全局容器使用
1.Autofac基础使用 参考: https://www.cnblogs.com/li150dan/p/10071079.html 2.ASP.NETCore 3.0 Autofac 容器替换 需要 ...
随机推荐
- Simple Robot Gym - 101102I (思维)
SaMer is building a simple robot that can move in the four directions: up (^), down (v), left (<) ...
- op挂载摄像头
挂载摄像头openwrt挂载ZC0301PL-USB摄像头教程http://www.openwrt.org.cn/bbs/viewthread.php?tid=30
- Nginx与PHP交互过程 + Nginx与PHP通信的两种方式
一.Nginx与PHP交互过程的7步走(用户对动态PHP网页访问过程) step1:用户将http请求发送给nginx服务器(用户和nginx服务器进行三次握手进行TCP连接) step2:nginx ...
- codeforces gym100801 Problem G. Graph
传送门:https://codeforces.com/gym/100801 题意: 给你一个DAG图,你最多可以进行k次操作,每次操作可以连一条有向边,问你经过连边操作后最小拓扑序的最大值是多少 题解 ...
- Java数据库操作学习
JDBC是java和数据库的连接,是一种规范,提供java程序与数据库的连接接口,使用户不用在意具体的数据库.JDBC类型:类型1-JDBC-ODBC桥类型2-本地API驱动类型3-网络协议驱动类型4 ...
- vs指定QT的工作目录(依赖第三方动态库时,这时vs编译出来后,运行会提示缺少动态库)good
当一个工程依赖第三方动态库时,这时vs编译出来后,运行会提示缺少动态库.解决方法: 项目->属性->调试: 工作目录:指定程序运行时的目录 环境:指定程序运行时的环境变量 我们可以在环境变 ...
- 驱动领域DDD的微服务设计和开发实战
你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案. 本文是基于 DDD 的微服务设计和开发实战篇,通过借鉴 ...
- 算法复杂度之 空间复杂度(Java)
0.说明 根据算法书上的定义,一个算法的空间复杂度包括算法程序所占用的空间,输入初始数据所占用的空间以及算法执行过程中所需要的额外空间.本文各种结论全部参考过标准文献,本人也进行过验证.验证过程本文不 ...
- 你确定你了解什么是linux系统?
1.什么是linux发行版 就Linux的本质来说,它只是操作系统的核心,负责控制硬件.管理文件系统.程序进程等,并不给用户提供各种工具和应用软件.所谓工欲善其事,被必先利其器,一套在优秀的操作系统核 ...
- 【原创】(十五)Linux内存管理之RMAP
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...