C# 和 .NET 中的 JSON 日志

作者:微信公众号:【架构师老卢】
7-3 14:48
158

如何充分利用日志?使用发送到可观测性堆栈的结构化日志!也许,最流行的运输格式是 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();
相关留言评论
昵称:
邮箱:
阅读排行