如何充分利用日志?使用发送到可观测性堆栈的结构化日志!也许,最流行的运输格式是 JSON。因此,从 .NET 6 开始,我们可以作为日志记录提供程序。在本文中,我们将研究它的确切工作原理,查看我们可以获得的不同日志,并了解另一件事。所以,直接进入代码!
日志记录功能不特定于任何 .NET 项目类型,并且对任何 .NET 项目类型都具有相同的作用。因此,让我们通过运行来启动最基本的一个:ASP .NET Core
dotnet new console
但是,与 不同的是,控制台应用程序不包含此功能。因此,我们需要安装一个单独的 nuget 包:ASP .NET Core
dotnet add package Microsoft.Extensions.Logging.Console
在 .NET 中,启动记录器是一个令人惊讶的扭曲过程。事实上,它被扭曲得如此之多,以至于最简单的方法是详细说明。下面是最基本的代码:
控制台日志提供程序似乎有某种延迟,因此对于日志实际出现,我们将等待 100 毫秒,然后再退出。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var logger = new ServiceCollection()
.AddLogging(l => l.AddJsonConsole())
.BuildServiceProvider()
.GetRequiredService<ILogger<Program>>();
logger.LogInformation("{Name} {Age}", "Egor", 27);
await Task.Delay(100); // Wait for logs to be written
如果我们的代码,我们将得到以下 JSONdotnet run
此日志的格式是为了便于阅读。实际的 JSON 日志将写入一行。但是,您可以通过将代码更改为以下内容来获取 JSON 格式:l.AddJsonConsole(c => c.JsonWriterOptions = new () { Indented = true })
{
"EventId": 0,
"LogLevel": "Information",
"Category": "object",
"Message": "Egor 27",
"State": {
"Message": "Egor 27",
"Name": "Egor",
"Age": 27,
"{OriginalFormat}": "{Name} {Age}"
}
}
现在让我们给事情增添一点趣味!如果不仅记录原语,还记录一些对象呢?让我们创建一个:
var egor = new Person("Egor", 28);
record Person(string Name, int Age);
为了让事情变得更有趣,假设我们有一些信息最初是作为 JSON 提供给我们的,例如作为请求正文,我们不知道它的类型。幸运的是,支持将此 json 反序列化为对象,这实际上是一个 .以下是我们将如何模仿它:JsonSerializerJsonElement
var hobby = JsonSerializer.Deserialize<object>("{\"Name\":\"Board Games\"}");
Logger 很乐意接受任何对象作为参数,因此让我们提交我们的信息以记录它:
logger.LogInformation("{Person} {Hobby}", egor, hobby);
这是我们现在将获得的日志:
{
"EventId": 0,
"LogLevel": "Information",
"Category": "Program",
"Message": "Person { Name = Egor, Age = 28 } {\u0022Name\u0022:\u0022Board Games\u0022}",
"State": {
"Message": "Person { Name = Egor, Age = 28 } {\u0022Name\u0022:\u0022Board Games\u0022}",
"Person": "Person { Name = Egor, Age = 28 }",
"Hobby": "{\u0022Name\u0022:\u0022Board Games\u0022}",
"{OriginalFormat}": "{Person} {Hobby}"
}
}
奇怪的是,我们没有得到任何嵌套的JSON对象。此外,我们得到了 和 的不同表示。但是,如果我们在这些对象上运行,我们实际上会将它们作为嵌套对象获得。那么,这是怎么回事呢?为了弄清楚这一点,让我们研究一下 .这是重要的部分:PersonhobbyJsonSerializer.SerializeJsonConsoleFormatter.cs
private static void WriteItem(Utf8JsonWriter writer, KeyValuePair<string, object> item)
{
string key = item.Key;
switch (item.Value)
{
case bool flag:
writer.WriteBoolean(key, flag);
break;
case byte num1:
writer.WriteNumber(key, (int) num1);
break;
// 9 different num cases omitted for brievity
case short num10:
writer.WriteNumber(key, (int) num10);
break;
case ushort num11:
writer.WriteNumber(key, (int) num11);
break;
case null:
writer.WriteNull(key);
break;
default:
writer.WriteString(key, JsonConsoleFormatter.ToInvariantString(item.Value));
break;
}
}
private static string ToInvariantString(object obj)
{
return Convert.ToString(obj, (IFormatProvider) CultureInfo.InvariantCulture);
}
这本质上意味着,如果我们不将一个项目识别为基元类型,我们将只对它运行一个并返回接收到的值,不允许对嵌套数据进行复杂的分析。ToString()
幸运的是,还有另一个名为 nuget 的 nuget,它为我们提供了另一个版本的 JSON 控制台日志记录。让我们来看看吧!Astor.Logging
dotnet add package Astor.Logging
该软件包与 Microsoft 的依赖注入系统完全兼容,因此我们唯一需要更改的行是:
.AddLogging(l => l.AddMiniJsonConsole())
另外,当然要添加。using Astor.Logging;
这是我们立即获得该包的日志:
{
"person": {
"name": "Egor",
"age": 28
},
"hobby": {
"Name": "Board Games"
}
}
所以现在,我们不只是得到嵌套的对象。我们还使我们的日志更加简约,因此得名。但是,如果我们需要一些元数据呢?让我们在记录器配置中使用。另外,请注意,记录器默认使用骆驼箱。这也可以使用 进行配置。让我们只是为了好玩而使用。最后,让我们来看看这个。因此,这是我们得到的代码:MiniIncludeAllSetNamingPolicyJsonNamingPolicy.KebabCaseLowerIndent
.AddLogging(l => l.AddMiniJsonConsole(j =>
j.IncludeAll().Indent().SetNamingPolicy(JsonNamingPolicy.KebabCaseLower)))
这是日志:
请注意,对于 初始 json,将保留大小写。hobby
{
"person": {"name":"Egor","age":28},
"hobby": {"Name":"Board Games"},
"log-original-format": "{Person} {Hobby}",
"log-category-name": "Program",
"log-level": "Information",
"log-event-id": 2,
"log-message": "Person { Name = Egor, Age = 28 } {\u0022Name\u0022:\u0022Board Games\u0022}"
}
在 .net 应用程序中添加 JSON 日志非常简单
logging.AddJsonConsole();
但是,您将得到一个充满元数据且没有嵌套功能的日志
{
"EventId": 0,
"LogLevel": "Information",
"Category": "object",
"Message": "Egor { Name = Board Games, Favorite = Resistance }",
"State": {
"Message": "Egor { Name = Board Games, Favorite = Resistance }",
"Name": "Egor",
"Hobby": "{ Name = Board Games, Favorite = Resistance }",
"{OriginalFormat}": "{Name} {Hobby}"
}
}
但是要获得一个简约的日志,其中包含您需要的内部对象,例如:
{
"name": "Egor",
"hobby": {"name":"Board Games","favorite":"Resistance"}
}
你只需要
dotnet add package Nist.Logs
并将您的代码更新为
logging.AddMiniJsonConsole();