参考源码:https://download.csdn.net/download/hefeng_aspnet/90084914
记录 ASP .NET Core http 请求和响应是几乎每个 .NET 开发人员迟早都会面临的常见任务。长期以来,开发团队选择的最流行的方法似乎是编写自定义中间件。但是,既然 .NET 6
我们有一个Microsoft.AspNetCore.HttpLogging
内置库。所以,亲爱的 .NET 海狸们,让我们看看微软给我们的日志吧!并且找到更好的。
我们的人工智能生成的吉祥物带有原木
设置日志记录
我们将从 .NET 提供的最简单的设置开始,运行以下控制台命令
dotnet new web
检查 http 请求的每个部分的日志记录非常重要,例如请求主体、查询字符串、错误和路径参数。因此默认Hello World
端点不行。让我们创建一个具有 blackjack 查询和路由参数的方:
app.MapPost("/parties/{partyId}/guests", (string partyId, [FromQuery] bool? loungeAccess, Guest visitor) => {
if (loungeAccess == true && !visitor.Vip)
throw new NotEnoughLevelException();
return new Ticket(
PartyId: partyId,
Receiver: visitor.Name,
LoungeAccess: loungeAccess ?? false,
Code: Guid.NewGuid().ToString()
);
});
app.Run();
public record Guest(string Name, bool Vip);
public record Ticket(string PartyId, string Receiver, bool LoungeAccess, string Code);
public class NotEnoughLevelException : Exception;
默认情况下,NotEnoughLevelException
ASP .NET Core 将粗略地中断请求并返回InternalServerError
。让我们改为返回一个错误对象。实现它的最简单方法可能是使用Nist.Errors
nuget 包。要安装它,我们将使用以下命令:
dotnet add package Nist.Errors
并将我们的异常映射到相应的错误:
app.UseErrorBody<Error>(ex => ex switch {
NotEnoughLevelException _ => new (HttpStatusCode.BadRequest, "NotEnoughLevel"),
_ => new (HttpStatusCode.InternalServerError, "Unknown")
}, showException: false);
现在,让我们开始记录请求和响应。最简单的启用方法包括 4 个步骤:
1. 注册http日志服务
builder.Services.AddHttpLogging(o => {});
2.附加http日志记录中间件
app.UseHttpLogging();
3. 删除appsettings.Development.json以尽量减少配置开销
4. 由于默认情况下的日志级别Microsoft.AspNetCore是Warning为 http 日志中间件指定专用的日志级别appsettings.json:
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
完成所有这些更改后,我们的完整版本Program.cs将如下所示:
using Microsoft.AspNetCore.Mvc;
using Nist.Errors;
using System.Net;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(o => {});
var app = builder.Build();
app.UseHttpLogging();
app.UseErrorBody<Error>(ex => ex switch {
NotEnoughLevelException _ => new (HttpStatusCode.BadRequest, "NotEnoughLevel"),
_ => new (HttpStatusCode.InternalServerError, "Unknown")
}, showException: false);
app.MapPost("/parties/{partyId}/guests", (string partyId, [FromQuery] bool? loungeAccess, Guest visitor) => {
if (loungeAccess == true && !visitor.Vip)
throw new NotEnoughLevelException();
return new Ticket(
PartyId: partyId,
Receiver: visitor.Name,
LoungeAccess: loungeAccess ?? false,
Code: Guid.NewGuid().ToString()
);
});
app.Run();
public record Guest(string Name, bool Vip);
public record Ticket(string PartyId, string Receiver, bool LoungeAccess, string Code);
public class NotEnoughLevelException : Exception;
让我们测试一下,从一个正常请求开始:
POST http://localhost:5244/parties/new-year/guests?loungeAccess=true
{
"name": "Paul",
"vip" : true
}
以下是我们得到的日志:
现在让我们讨论一个“糟糕的”请求。
POST http://localhost:5244/parties/halloween/guests?loungeAccess=true
{
"name": "John",
"vip" : false
}
我们将获得:
配置日志记录
如您所见,尽管我们记录了很多内容,但我们无法真正说出有关请求的很多信息。我们可能会找出哪个端点被击中以及我们的响应是否成功(如果我们使用 http 状态代码),但仅此而已。此外,在高负载环境中,匹配请求和响应可能是一个挑战,因为它们是分开记录的。所以让我们CombineLogs看看我们可以记录什么。以下是代码:
builder.Services.AddHttpLogging(o => {
o.CombineLogs = true;
o.LoggingFields = HttpLoggingFields.All | HttpLoggingFields.RequestQuery;
});
HttpLoggingFields.All有点撒谎,日志中也明确指出:“HttpRequest.QueryString 不包含在该标志中,因为它可能包含私人信息”。因此我们需要手动附加 RequestQuery
这就是我们现在得到的:
这样就好多了,现在我们可以看到到底收到了什么、响应了什么,以及处理花了多少时间。然而,日志仍然让人感到不知所措——我们记录了很多标头,但似乎没有任何价值。让我们只留下我们需要的东西:
builder.Services.AddHttpLogging(o => {
o.CombineLogs = true;
o.LoggingFields = HttpLoggingFields.RequestQuery
| HttpLoggingFields.RequestMethod
| HttpLoggingFields.RequestPath
| HttpLoggingFields.RequestBody
| HttpLoggingFields.ResponseStatusCode
| HttpLoggingFields.ResponseBody
| HttpLoggingFields.Duration;
});
现在,我们将以更为紧凑的格式获得几乎相同数量的有用信息:
使其变得更好
然而,我们现有的技术仍然存在一些问题:
1、请注意,由于我们有路径变量,因此partyId字段的值path对于单个端点来说并不相同。这将阻止我们通过特定端点获取分析数据。
2、请求响应日志与发生异常的日志之间没有任何联系。这会使查找失败响应的确切异常变得复杂。特别是当异常无法识别时。
3、由于多种原因,日志仍然占用大量空间。首先,我们有无用的不可移除字段(RequestBodyStatus, PathBase)。其次,我们的格式非常“宽”,总共使用 4 行来表示方法、路径和查询字符串。
幸运的是,我们有一个 nuget 包可以弥补内置库的盲点。我们来看看吧。首先,我们需要安装它:
dotnet add package Nist.Logging
然后只需更改三行:
using Nist.Logs;
// remove builder.Services.AddHttpLogging
app.UseHttpIOLogging(); // instead of app.UseHttpLogging();
我们得到了最少但功能齐全的日志:
尽管仍然记录发生的异常,但我们不再需要依赖它,因为我们在日志中间件ExceptionHandlerMiddleware中记录了异常。您甚至可以ExceptionHandlerMiddleware在"Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None"appsettings.json
回顾
最后,让我们看一下我们能够获得的最佳请求 - 响应日志:
尽管Microsoft.AspNetCore.NET 6 提供了广泛的开箱即用的 http 日志记录功能,但仍缺少一些功能。因此,我们使用了Nist.Logsnuget 包。下表总结了库的功能列表:
| Feature | Microsoft.AspNetCore.HttpLogging | Nist.Logs |
|-------------------- |--------------------------------------|----------- |
| Full Uri | ❌ - Only Path and Query Separately | ✅ |
| Minimalism | ❌ - Few unremovable redundant fields | ✅ |
| Exception attached | ❌ | ✅ |
| Endpoint id | ❌ | ✅ |
| Single Line | ✅ | ✅ |
| Http Method | ✅ | ✅ |
| Request Body | ✅ | ✅ |
| Response Body | ✅ | ✅ |
| Response Code | ✅ | ✅ |
| Request Duration | ✅ | ✅ |
以下是最终版本,供快速参考Program.cs:
using Microsoft.AspNetCore.Mvc;
using Nist.Logs;
using Nist.Errors;
using System.Net;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpIOLogging();
app.UseErrorBody<Error>(ex => ex switch {
NotEnoughLevelException _ => new (HttpStatusCode.BadRequest, "NotEnoughLevel"),
_ => new (HttpStatusCode.InternalServerError, "Unknown")
}, showException: false);
app.MapPost("/parties/{partyId}/guests", (string partyId, [FromQuery] bool? loungeAccess, Guest visitor) => {
if (loungeAccess == true && !visitor.Vip)
throw new NotEnoughLevelException();
return new Ticket(
PartyId: partyId,
Receiver: visitor.Name,
LoungeAccess: loungeAccess ?? false,
Code: Guid.NewGuid().ToString()
);
});
app.Run();
public record Guest(string Name, bool Vip);
public record Ticket(string PartyId, string Receiver, bool LoungeAccess, string Code);
public class NotEnoughLevelException : Exception;
项目代码:https://download.csdn.net/download/hefeng_aspnet/90084914
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。
标签:Core,ASP,string,app,我们,loungeAccess,new,NET,日志 From: https://blog.csdn.net/hefeng_aspnet/article/details/144255338