Serilog高级玩法之用Serilog记录所选终结点附加属性
这是在ASP.NET Core 3.X中使用Serilog.AspNetCore系列文章的第二篇文章:。
- 第1部分-使用Serilog RequestLogging减少日志详细程度
- 第2部分-使用Serilog记录所选的终结点属性(本文)
- 第3部分-使用Serilog.AspNetCore记录MVC属性
- 第4部分-从Serilog请求日志记录中排除健康检查端点
作者:依乐祝
在我的上一篇文章中,我描述了如何配置Serilog的RequestLogging中间件为每个请求创建“摘要”日志,以替换默认情况下从ASP.NET Core获取的10个或更多日志。
在本文中,我将展示如何向Serilog的摘要请求日志中添加其他元数据,例如请求的主机名,响应的内容类型或从ASP.NET Core 3.0中使用的终结点路由中间件所选择的端点名称。
ASP.NET Core基础结构日志很详细,但是默认情况下具有太多详细信息
正如我在上一篇文章(https://andrewlock.net/using-serilog-aspnetcore-in-asp-net-core-3-reducing-log-verbosity/)中所展示的那样,在开发环境中,ASP.NET Core基础架构将为每一个RazorPage处理程序生成10条日志消息:
通过安装了Serilog.AspNetCore的 NuGet包后并引入RequestLoggingMiddleware
之后,可以将其精简为一条日志消息:
本文中使用的所有日志图片均来自一款优秀的为结构化日志提供可视化界面的工具-Seq
显然,原始的日志集更加冗长,并且其中大部分不是特别有用的信息。但是,如果您将原始的10条日志作为一个整体来看,则与Serilog摘要日志相比,它们确实会在结构日志模板中记录一些其他属性。
由ASP.NET Core基础结构记录的而Serilog 未记录的扩展内容包括(下面这些还是英文的看着顺眼):
- Host (
localhost:5001
) - Scheme (
https
) - Protocol (
HTTP/2
) - QueryString (
test=true
) - EndpointName (
/Index
) - HandlerName (
OnGet
/SerilogRequestLogging.Pages.IndexModel.OnGet
) - ActionId (
1fbc88fa-42db-424f-b32b-c2d0994463f1
) - ActionName (
/Index
) - RouteData (
{page = "/Index"}
) - ValidationState (
True
/False
) - ActionResult (
PageResult
) - ContentType (
text/html; charset=utf-8
)
我认为如果要把上述属性中的其中一些包含在摘要日志消息中,将非常有用。例如,如果您的应用程序绑定到多个主机名,那么Host
绝对是重要的日志。QueryString
可能是另一个有用的字段。EndpointName
/ HandlerName
,ActionId
并且ActionName
似乎不那么重要,因为您应该能够推断出给定的请求路径,但是显式记录它们将帮助您更加方便的捕获错误,并使过滤针对特定操作的所有请求变得更加容易。
概括地说,您可以将这些属性分为两类:
- 请求/响应特性:如
Host
,Scheme
,ContentType
,QueryString
,EndpointName
- MVC / RazorPages相关的属性:如
HandlerName
,ActionId
,ActionResult
等
在这篇文章中,我将展示如何添加这些类别中的第一种,即与请求/响应相关的属性,在下一篇文章中,我将展示如何添加基于MVC / RazorPages的属性。
向Serilog请求日志添加扩展数据
在上一篇文章中,我展示了如何将Serilog请求日志记录添加到您的应用程序中,因此在此不再赘述。现在,我假设您已经进行了设置,并且您拥有一个包含以下内容的Startup.Configure
方法:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... Error handling/HTTPS middleware
app.UseStaticFiles();
app.UseSerilogRequestLogging(); // <-- Add this line
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
该UseSerilogRequestLogging()
扩展方法将Serilog RequestLoggingMiddleware
添加到请求管道中。您还可以通过调用重载来配置RequestLoggingOptions
的实例。此类具有几个属性,可以让您自定义请求记录器如何生成日志语句:
public class RequestLoggingOptions
{
public string MessageTemplate { get; set; }
public Func<HttpContext, double, Exception, LogEventLevel> GetLevel { get; set; }
public Action<IDiagnosticContext, HttpContext> EnrichDiagnosticContext { get; set; }
}
该MessageTemplate
属性控制将日志呈现为的字符串格式,GetLevel
允许您控制给定日志索要记录的级别,如 Debug
/ Info
/ Warning
等。这里我们所关心的是EnrichDiagnosticContext
属性。
设置了该属性的Action<>
之后,在生成日志消息时它将被Serilog中间件调用并执行。它在日志写入之前运行,这意味着它在中间件管道执行之后运行。例如,在下图中(取自我的书《 ASP.NET Core in Action》),当响应“回传”到中间件管道时,在第5步写入日志:
在管道处理之后写入日志这一事实意味着两件事:
- 我们可以访问Response的属性,例如状态码,经过的时间或内容类型
- 我们可以访问在管道后面设置的中间件的功能,例如,由
EndpointRoutingMiddleware
(通过UseRouting()
添加的)设置的功能:IEndpointFeature
在下一部分中,我将提供一个帮助程序功能,该功能会将所有“缺少”属性添加到Serilog请求日志消息中。
在IDiagnosticContext中设置扩展值
Serilog.AspNetCore会将接口IDiagnosticContext
作为单例添加到DI容器中,因此您可以从任何类中访问它。然后,您可以调用Set()
方法,将其他属性附加到请求日志消息中。
例如,如文档所示,您可以从操作方法中添加任意值:
public class HomeController : Controller
{
readonly IDiagnosticContext _diagnosticContext;
public HomeController(IDiagnosticContext diagnosticContext)
{
_diagnosticContext = diagnosticContext;
}
public IActionResult Index()
{
// The request completion event will carry this property
_diagnosticContext.Set("CatalogLoadTime", 1423);
return View();
}
}
然后,结果摘要日志将包含属性CatalogLoadTime
。
RequestLoggingOptions
通过设置所提供IDiagnosticContext
实例的值,我们基本上使用完全相同的方法来定制中间件所使用的方法。下面的静态帮助器类从当前HttpContext
上下文检索值,并在值可用时对其进行设置。
下面的静态helper类从当前HttpContext检索值,并在值可用时设置它们。
public static class LogHelper
{
public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext)
{
var request = httpContext.Request;
// Set all the common properties available for every request
diagnosticContext.Set("Host", request.Host);
diagnosticContext.Set("Protocol", request.Protocol);
diagnosticContext.Set("Scheme", request.Scheme);
// Only set it if available. You're not sending sensitive data in a querystring right?!
if(request.QueryString.HasValue)
{
diagnosticContext.Set("QueryString", request.QueryString.Value);
}
// Set the content-type of the Response at this point
diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
// Retrieve the IEndpointFeature selected for the request
var endpoint = httpContext.GetEndpoint();
if (endpoint is object) // endpoint != null
{
diagnosticContext.Set("EndpointName", endpoint.DisplayName);
}
}
}
上面的帮助器函数从“Request”,“Response”以及其他中间件(端点名称)设置的功能中检索值。您可以扩展它,以根据需要在请求中添加其他值。
您可以在你的Startup.Configure()
方法中通过调用UseSerilogRequestLogging
的EnrichDiagnosticContext
属性,来注册上面的帮助类:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... Other middleware
app.UseSerilogRequestLogging(opts
=> opts.EnrichDiagnosticContext = LogHelper.EnrichFromRequest);
// ... Other middleware
}
现在,当您发出请求时,您将看到添加到Serilog结构化日志中的所有其他属性:
只要您具有通过当前HttpContext可供中间件管道使用的值,就可以使用此方法。但是MVC的相关属性是个例外,它们是MVC中间件“内部”的特性,例如action 名称或RazorPage处理程序名称。在下一篇文章中,我将展示如何将它们添加到Serilog请求日志中。
总结
默认情况下,用Serilog的请求日志记录中间件替换ASP.NET Core基础结构日志记录时,与开发环境的默认日志记录配置相比,您会丢失一些信息。在本文中,我展示了如何通过自定义Serilog RequestLoggingOptions
来添加这些附加属性。
这样的做法非常简单-您可以访问HttpContext
,因此你可以检索它包含的任何可用的值,并将它们设置为IDiagnosticContext
所提供的属性。这些属性将作为附加属性添加到Serilog生成的结构化日志中。在下一篇文章中,我将展示如何将MVC特定的属性值添加到请求日志中。敬请期待吧!
Serilog高级玩法之用Serilog记录所选终结点附加属性的更多相关文章
- 轻量级高性能ORM框架:Dapper高级玩法
Dapper高级玩法1: 数据库中带下划线的表字段自动匹配无下划线的Model字段. Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 备 ...
- 【ASP.NET Core】依赖注入高级玩法——如何注入多个服务实现类
依赖注入在 ASP.NET Core 中起中很重要的作用,也是一种高大上的编程思想,它的总体原则就是:俺要啥,你就给俺送啥过来.服务类型的实例转由容器自动管理,无需我们在代码中显式处理. 因此,有了依 ...
- 【Python基础】random 的高级玩法
random 模块的高级玩法 1.python 随机产生姓名 方式一: import random xing = [ '赵', '钱', '孙', '李', '周', '吴', '郑', '王', ' ...
- Word 查找替换高级玩法系列之 -- 段首批量添加字符
打开「查找和替换」输入框,按照下图操作: 更多查找替换高级玩法,参看:Word查找替换高级玩法系列 -- 目录篇 未完 ...... 点击访问原文(进入后根据右侧标签,快速定位到本文)
- Word 查找替换高级玩法系列之 -- 把论文中的缩写词快速变成目录下边的注释表
1. 前言 问题:Word写论文如何把文中的缩写快速转换成注释表? 原来样子: 想要的样子: 2. 步骤 使用查找替换高级用法,替换缩写顺序 选中所有文字 打开查找替换对话框,输入以下表达式: 替换后 ...
- maven 高级玩法
maven 高级玩法 标签(空格分隔): maven 实用技巧 Maven 提速 多线程 # 用 4 个线程构建,以及根据 CPU 核数每个核分配 1 个线程进行构建 $ mvn -T 4 clean ...
- 十五天精通WCF——第九天 高级玩法之自定义Behavior
终于我又看完了二期爱情保卫战,太酸爽了,推荐链接:http://www.iqiyi.com/a_19rrgublqh.html?vfm=2008_aldbd,不多说,谁看谁入迷,下面言归正传, 看看这 ...
- [转]十五天精通WCF——第九天 高级玩法之自定义Behavior
终于我又看完了二期爱情保卫战,太酸爽了,推荐链接:http://www.iqiyi.com/a_19rrgublqh.html?vfm=2008_aldbd,不多说,谁看谁入迷,下面言归正传, 看看这 ...
- awk高级玩法
1. 程序元素 一个awk 程序是一对以模式(pattern) 与大括号框起来的操作(action) 组合而成的,或许,还会加上实现操作细节的函数(function ) .针对每个匹配于输人数据的模式 ...
随机推荐
- java打包上传服务器的一些命令
Maven下package打包成jar包和war包,都在target目录下 其中War包扔在tomcat的webapps目录下.随tomcat启动自行启动 运行jar包命令. nohup java - ...
- 关于react打包之后静态资源加载错误的问题
之前在打包react项目时发现一些问题,打包出来后我的一部分png图标加载不出来,开发者模式发现他们的路径中莫名其妙混入了我在react-router路由中使用<Browserrouter> ...
- Maven工程
maven:父子工程 1,父工程并没有实质性的内容,所有的jar包都在里面,也就是说只需要管理jar包即可,不参与任务方法逻辑 2,在父工程中拥有很多的子模块,每一个子模块都代表了不用的包如(pack ...
- QP移植
以STM32平台为例,该单片机的ARM Cortex-M系列内核正是被QP长期支持,所以QP在ARM Cortex-M系列内核上已经有长时间的应用验证. 在配套书籍PSICC2中的例程为QP最原始的版 ...
- 第二阶段:1.流程图:9.excel绘制甘特图
后面的框都是日期 可以以一个月为周期计算或者周或者... 因为产品经理应该严格把控产品的时间 因此甘特图特别有必要 注意:任务拆解的越细 把控度越强 然后对格式进行设置 注意时间下面可以用颜色填充来表 ...
- java_学生成绩管理系统
//信1805-2 20183670 王云鹏 package student; import java.util.Scanner; public class ScoreManagement { sta ...
- jsp页面中如何让一个输入框内的提示文字是灰色而输入的文字是黑色
<input name="name" type="text" value="提示的文字" size="30" o ...
- ELK学习实验001:Elastic Stack简介
1 背景介绍 在我们日常生活中,我们经常需要回顾以前发生的一些事情:或者,当出现了一些问题的时候,可以从某些地方去查找原因,寻找发生问题的痕迹.无可避免需要用到文字的.图像的等等不同形式的记录.用计算 ...
- Mysql库、表、记录的基本操作
库的操作 ---> 类似于文件夹 - 增: 创建数据库: create database db1; 创建带字符集的数据库: create database db2 charset=utf8; - ...
- 浅谈Java的默认和静态方法
目录 浅谈Java的默认和静态方法 Java新增默认方法有啥用 Java新增的静态方法有啥用 浅谈Java的默认和静态方法 允许在接口中声明默认方法和静态方法,是JDK1.8新增的特性.存在即合理,两 ...