如何从Serilog请求日志记录中排除健康检查终结点
这是在ASP.NET Core 3.X中使用Serilog.AspNetCore系列文章的第四篇文章:。
- 第1部分-使用Serilog RequestLogging减少日志详细程度
- 第2部分-使用Serilog记录所选的终结点属性
- 第3部分-使用Serilog.AspNetCore记录MVC属性
- 第4部分-从Serilog请求日志记录中排除健康检查端点(本文)
作者:依乐祝
在本系列的前几篇文章中,我描述了如何配置Serilog的RequestLogging中间件以向Serilog的请求日志摘要中添加附加属性,例如请求主机名或选定的端点名称。我还展示了如何使用过滤器将MVC或RazorPage特定的属性添加到摘要日志。
在本文中,我将展示如何过滤掉某个特定请求的摘要日志消息。当您有一个访问比较频繁的端点时,这非常有用,因为为每个请求都进行记录几乎没有什么价值。
健康检查访问较频繁
这篇文章的动机来自我们在Kubernetes中运行应用程序时看到的行为。Kubernetes使用两种类型的“健康检查”(或“探针”)来检查应用程序是否正常运行:liveness probes和readiness probes。您可以将探测配置为向应用程序发出HTTP请求,作为应用程序正常运行的指示器。
从Kubernetes 1.16版开始,存在第三种探针,即startup probe。
在ASP.NET Core 2.2+中提供的健康检查终结点非常适合这些探针。您可以设置一个简单,没有任何返回值的健康检查,该健康检查对每个请求返回200 OK的响应,以使Kubernetes知道您的应用程序没有崩溃。
在ASP.NET Core 3.x中,可以使用终结点路由来配置健康检查。您必须在Startup.cs中的ConfigureServices中通过调用AddHealthChecks()来添加必须的服务,并在Configure中使用MapHealthChecks()来添加健康检查终结点:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// ..other service configuration
services.AddHealthChecks(); // Add health check services
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// .. other middleware
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health"); //Add health check endpoint
endpoints.MapControllers();
});
}
}
在上面的示例中,向/healthz发送请求将调用健康检查终结点。由于我没有配置任何运行状况检查200,因此只要应用程序正在运行,端点将始终返回响应:
在上面的示例中,向/healthz发送请求将调用运行状况检查终结点。由于我没有配置任何运行的健康检查,因此只要应用程序正在运行,端点将始终返回200响应:

这里存在的唯一的问题是Kubernetes将非常频繁的调用这个终结点。当然,确切的频率由您决定,但每10秒检查一次应该是很常见的。但是如果你想让Kubernetes可以快速重启有故障的Pod的话,您就需要一个相对较高的频率了。
这本身不是问题;Kestrel每秒可以处理数百万个请求,因此这不是性能问题。这里令人比较烦恼的问题是每个请求都会生成一定数量的日志。虽然它没有MVC基础架构的请求所示的那么多-每个请求10个日志,但是即使每个请求只有1个日志(就像我们从Serilog.AspNetCore获得的那样)都可能会令人不快。
这里的主要问题是成功进行健康检查请求的日志实际上并未告诉我们任何有用的信息。它们与任何业务活动都不相关,它们纯粹是基础设施。这里如果能够跳过这些请求的Serilog请求摘要日志会很好。在下一部分中,我将介绍我所想出的方法,该方法依赖于本系列前面几篇文章的内容,并在其基础上做出更改。
定制用于Serilog请求日志的日志级别
在上一篇文章中,我展示了如何在Serilog请求日志中包括所选终结点。我的方法是在注册Serilog中间件时为RequestLoggingOptions.EnrichDiagnosticContext属性提供一个自定义函数
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... Other middleware
app.UseSerilogRequestLogging(opts
// EnrichFromRequest helper function is shown in the previous post
=> opts.EnrichDiagnosticContext = LogHelper.EnrichFromRequest);
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/healthz"); //Add health check endpoint
endpoints.MapControllers();
});
}
RequestLoggingOptions具有另一个属性,GetLevel该属性的Func<>被用于确定应用于给定请求日志的日志记录级别。默认情况下,它设置为以下功能:
public static class SerilogApplicationBuilderExtensions
{
static LogEventLevel DefaultGetLevel(HttpContext ctx, double _, Exception ex) =>
ex != null
? LogEventLevel.Error
: ctx.Response.StatusCode > 499
? LogEventLevel.Error
: LogEventLevel.Information;
}
此函数检查是否为请求引发了异常,或者响应代码是否为5xx错误。如果是这样,它将创建一个Error级别的摘要日志,否则将创建一个Information级别日志。
假设您希望将摘要日志记录为Debug而不是Information。首先,您将创建一个具有以下所需逻辑的辅助函数,如下所示:
public static class LogHelper
{
public static LogEventLevel CustomGetLevel(HttpContext ctx, double _, Exception ex) =>
ex != null
? LogEventLevel.Error
: ctx.Response.StatusCode > 499
? LogEventLevel.Error
: LogEventLevel.Debug; //Debug instead of Information
}
然后,您可以在调用时设置级别功能UseSerilogRequestLogging():
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... Other middleware
app.UseSerilogRequestLogging(opts => {
opts.EnrichDiagnosticContext = LogHelper.EnrichFromRequest;
opts.GetLevel = LogHelper.CustomGetLevel; // Use custom level function
});
//... other middleware
}
现在,您的请求摘要日志将全部记录为Debug,除非发生错误(Seq的屏幕截图):

但这如何解决我们的冗长日志的问题呢?
当你在配置Serilog时,你通常应该会定义一个最低请求级别。例如,以下简单配置将默认级别设置为Debug(),并将其写入控制台接收器:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
因此,过滤日志的最简单方法是使日志级别低于MinimumLevel记录器配置中指定的级别。一般而言,如果使用最低级别Verbose,它将几乎总是被过滤掉。
困难之处在于我们不想总是将Verbose用作摘要日志的日志级别。如果这样做,我们将不会获得任何非错误的请求日志,而Serilog中间件将变得毫无意义!
相反,我们希望将日志级别设置为Verbose 仅针对运行健康检查端点的请求。在下一节中,我将展示如何在不影响其他请求的情况下识别这些请求。
将自定义日志级别用于健康检查终结点请求
我们需要的是能够在写入摘要日志时识别出健康检查的请求的能力。如前所示,该GetLevel()方法将当前HttpContext作为参数,因此理论上有一些可行性。对我来说,最明显的做法是:
- 将
HttpContext.Request路径与已知的健康检查路径列表进行比较 - 当健康检查终结点被请求时,使用选定的端点元数据来进行标识
第一种选择是最明显的,但是它真的不值得尝试。一旦你陷入其中,你会发现你必须开始复制请求路径并处理各种边缘情况,因此在这里我将跳过该情况。
第二种方法使用了与我上一篇文章中使用的方法类似,在该方法中,我们获得了EndpointRoutingMiddleware为给定请求选择的IEndpointFeature。此功能(如果存在)提供了所选端点的显示名称和路由数据等详细信息。
如果我们假设健康检查是使用默认显示名称注册的,即"Health checks",则我们可以使用HttpContext来标识“健康检查”的请求,如下所示:
public static class LogHelper
{
private static bool IsHealthCheckEndpoint(HttpContext ctx)
{
var endpoint = ctx.GetEndpoint();
if (endpoint is object) // same as !(endpoint is null)
{
return string.Equals(
endpoint.DisplayName,
"Health checks",
StringComparison.Ordinal);
}
// No endpoint, so not a health check endpoint
return false;
}
}
我们可以将此功能与默认GetLevel功能的自定义版本结合使用,以确保运行健康检查请求的摘要日志使用Verbose级别,当发生错误时使用Error而其他请求则使用Information:
public static class LogHelper
{
public static LogEventLevel ExcludeHealthChecks(HttpContext ctx, double _, Exception ex) =>
ex != null
? LogEventLevel.Error
: ctx.Response.StatusCode > 499
? LogEventLevel.Error
: IsHealthCheckEndpoint(ctx) // Not an error, check if it was a health check
? LogEventLevel.Verbose // Was a health check, use Verbose
: LogEventLevel.Information;
}
}
这个嵌套的三目运算符有一个额外的逻辑-对于无错误,我们检查是否选择了显示名为“Health check”的端点,如果选择了,则使用级别Verbose,否则使用Information。
您可以进一步推广此代码,以允许传入其他显示名称或其他自定义使用的日志级别。为了简单起见,我在这里没有这样做,但是GitHub上的相关示例代码显示了如何执行此操作。
剩下的就是更新Serilog中间件RequestLoggingOptions以使用您的新功能:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... Other middleware
app.UseSerilogRequestLogging(opts => {
opts.EnrichDiagnosticContext = LogHelper.EnrichFromRequest;
opts.GetLevel = LogHelper.ExcludeHealthChecks; // Use the custom level
});
//... other middleware
}
这时候当你运行应用程序后检查日志时,您会看到标准请求的普通请求日志,但没有健康检查的日志(除非发生错误!)。在下面的屏幕截图中,我将Serilog配置为也记录Verbose日志,以便您可以查看运行状况检查请求-通常会将它们过滤掉!

总结
在本文中,我展示了如何为Serilog中间件的RequestLoggingOptions提供一个自定义函数,该函数定义了要为给定请求的日志使用的LogEventLevel。例如,我展示了如何使用它将默认级别更改为Debug。如果您选择的级别低于最低级别,它将被完全过滤掉,并且不会被记录。
我还展示了您可以使用这种方法来过滤通过调用健康检查端点生成的公共(低级别的)请求日志。一般来说,这些请求只有在指出问题时才有意义,但它们通常也会在成功时生成请求日志。由于这些端点被频繁调用,因此它们可以显著增加写入的日志数量(无用)。
本文中的方法是检查选定的IEndpointFeature并检查它是否具有显示名称“Health checks”。如果是,请求日志将使用Verbose级别写入,这通常会被过滤掉。为了更灵活,您可以自定义在这个帖子中显示的日志来处理多个端点名称,或者任何其他的标准。
如何从Serilog请求日志记录中排除健康检查终结点的更多相关文章
- 论文系统Step1:从日志记录中提取特定信息
论文系统Step1:从日志记录中提取特定信息 前言 论文数据需要,需要实现从服务器日志中提取出用户的特定交互行为信息.日志内容如下: 自己需要获取"请求数据包一行的信息"及&quo ...
- 如何在 ASP.Net Core 中实现 健康检查
健康检查 常用于判断一个应用程序能否对 request 请求进行响应,ASP.Net Core 2.2 中引入了 健康检查 中间件用于报告应用程序的健康状态. ASP.Net Core 中的 健康检查 ...
- 在.NET Core 中实现健康检查
.NET Core中提供了开箱即用的运行状况检查,首先,我将在.NET Core API应用程序中执行运行状况检查,接下来,我们将使用DbContext集成SQL Server或数据库的运行状况检查, ...
- 在 ASP.NET Core 中使用 Serilog 进行日志记录
目录 从 NuGet 安装 Serilog 在 Main函数 中配置 Serilog 在项目中使用 Serilog 进行日志输出 从 NuGet 安装 Serilog 核心的包是 Serilog 和 ...
- .Net Core 3.0 使用 Serilog 把日志记录到 SqlServer
Serilog简介 Serilog是.net中的诊断日志库,可以在所有的.net平台上面运行.Serilog支持结构化日志记录,对复杂.分布式.异步应用程序的支持非常出色.Serilog可以通过插件的 ...
- springcloud zuulfilter 实现get,post请求日志记录功能
import com.alibaba.fastjson.JSONObject; import com.idoipo.infras.gateway.open.model.InvokeLogModel; ...
- K8s中Pod健康检查源代码分析
了解k8s中的Liveness和Readiness Liveness: 表明是否容器正在运行.如果liveness探测为fail,则kubelet会kill掉容器,并且会触发restart设置的策略. ...
- Kubernetes中Pod健康检查
目录 1.何为健康检查 2.探针分类 2.1.LivenessProbe探针(存活性探测) 2.2.ReadinessProbe探针(就绪型探测) 3.探针实现方法 3.1.Container Exe ...
- ASP.NET CORE在docker中的健康检查(healthcheck)
在使用docker-compose的过程中,很多程序都提供了健康检查(healthcheck)的方法,通过健康检查,应用程序能够在确保其依赖的程序都已经启动的前提下启动,减少各种错误的发生,同时,合理 ...
随机推荐
- BGP团体属性的应用案例
XRV1 ===================================================================== version 15.5service times ...
- hibernate_检索策略
一.概述 检索策略分三大块,类级别检索策略和关联级别检测策略. 类级别检索策略:get.load. 关联级别检索策略:order.getCustomer().getName() 上面这两种应该是看得懂 ...
- vue新增属性响应式更新的问题
根据官方文档定义: 如果在实例创建之后添加新的属性到实例上,它不会触发视图更新. 受现代 JavaScript 的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删 ...
- SQL Server内连接、左外连接、右外连接、完全连接、交叉lianjie
数据准备: create table T1( A ) not null, B ) not null, C tinyint not null ); create table T2( B ) not nu ...
- 定位、识别;目标检测,FasterRCNN
定位: 针对分类利用softmax损失函数,针对定位利用L2损失函数(或L1.回归损失等) 人关节点检测 针对连续变量和离散变量需要采用不同种类的损失函数. 识别: 解决方案: 1.利用滑动窗口,框的 ...
- [Vue源码]一起来学Vue模板编译原理(一)-Template生成AST
本文我们一起通过学习Vue模板编译原理(一)-Template生成AST来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vu ...
- linux下安装MariaDB数据库
搜素某个文件:find / -name '文件名(或文件夹名)' 1.编辑yum源:vi /etc/yum.repos.d/MariaDB.repo 2.编辑文件时用到的vi命令: vi 打开一个不存 ...
- leetcode.1266访问所有点的最小时间
平面上有 n 个点,点的位置用整数坐标表示 points[i] = [xi, yi].请你计算访问所有这些点需要的最小时间(以秒为单位). 你可以按照下面的规则在平面上移动: 每一秒沿水平或者竖直方向 ...
- 快速傅里叶变换与快速数论变换瞎学笔记$QwQ$
$umm$先预警下想入门$FFT$就不要康我滴学习笔记了,,, 就,我学习笔记基本上是我大概$get$之后通过写$blog$加强理解加深记忆这样儿的,有些姿势点我可能会直接$skip$什么的,所以对除 ...
- [白话解析] 深入浅出一致性Hash原理
[白话解析] 深入浅出一致性Hash原理 0x00 摘要 一致性哈希算法是分布式系统中常用的算法.但相信很多朋友都是知其然而不知其所以然.本文将尽量使用易懂的方式介绍一致性哈希原理,并且通过具体应用场 ...