Intro
.NET 从 5.0 开始支持了 console formatter,首次支持了 json console, 自定义 console 日志也变得可能,可以参考之前的介绍 .net 5.0 中的 JsonConsole
然而 .NET 里的 json console 里的 json 编码要求是比较严格的,有一些特殊符号和特殊编码会有点问题,我们需要使用 JavaScriptEncoder.UnsafeRelaxedJsonEscaping
才能正常显式,于是想着直接基于 newtonsoft json 来实现一个 console log formatter.
Implement
参考 JsonConsoleFormatter
可以实现基于 NewtonsoftJson
的 NewtonJsonFormatter
,实现如下:
public sealed class NewtonJsonFormatterOptions: ConsoleFormatterOptions
{
}
public sealed class NewtonJsonFormatter: ConsoleFormatter
{
public const string FormatterName = "NewtonJson";
private readonly NewtonJsonFormatterOptions _options;
public NewtonJsonFormatter(IOptions<NewtonJsonFormatterOptions> options) : base(FormatterName)
{
_options = options.Value;
}
public override void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeProvider? scopeProvider, TextWriter textWriter)
{
var message = logEntry.Formatter(logEntry.State, logEntry.Exception);
if (logEntry.Exception == null && message == null)
{
return;
}
JsonWriter writer = new JsonTextWriter(textWriter);
writer.WriteStartObject();
if (_options.TimestampFormat != null)
{
writer.WritePropertyName("Timestamp");
var timestamp = _options.UseUtcTimestamp ? DateTimeOffset.UtcNow : DateTimeOffset.Now;
var timestampText = timestamp.ToString(_options.TimestampFormat);
writer.WriteValue(timestampText);
}
writer.WritePropertyName("Level");
writer.WriteValue(logEntry.LogLevel.ToString());
writer.WritePropertyName("EventId");
writer.WriteValue(logEntry.EventId.ToString());
writer.WritePropertyName(nameof(logEntry.Category));
writer.WriteValue(logEntry.Category);
writer.WritePropertyName("Message");
writer.WriteValue(message);
if (logEntry.Exception != null)
{
writer.WritePropertyName("Exception");
writer.WriteValue(logEntry.Exception);
}
if (logEntry.State != null)
{
writer.WritePropertyName(nameof(logEntry.State));
writer.WriteStartObject();
writer.WritePropertyName("Message");
writer.WriteValue(logEntry.State.ToString());
if (logEntry.State is System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>> stateProperties)
{
foreach (var item in stateProperties)
{
writer.WritePropertyName(item.Key);
writer.WriteValue(item.Value);
}
}
writer.WriteEndObject();
}
writer.WriteEndObject();
writer.Flush();
textWriter.WriteLine();
}
}
formatter 的设计需要有一个 options 所以我们需要新加一个 NewtonJsonFormatterOptions
为了使用起来方便,我们定义一个扩展方法 AddNewtonJsonConsole
:
public static partial class LoggingBuilderExtensions
{
public static ILoggingBuilder AddNewtonJsonConsole(this ILoggingBuilder loggingBuilder,
Action<NewtonJsonFormatterOptions>? optionsConfigure = null)
{
loggingBuilder.AddConsoleFormatter<NewtonJsonFormatter, NewtonJsonFormatterOptions>();
loggingBuilder.AddConsole(options => options.FormatterName = NewtonJsonFormatter.FormatterName);
if (optionsConfigure != null)
{
loggingBuilder.Services.Configure(optionsConfigure);
}
return loggingBuilder;
}
}
首先我们需要通过 AddConsoleFormatter
来注册我们自定义的 NewtonJsonFormatter
,
然后使用 AddConsole
注册 console 服务并指定 FormatterName
,另外可以通过一个可选的委托来自定义一些配置
Sample
来看一个简单的使用示例:
使用默认的 JsonConsole
时
var builder = Host.CreateEmptyApplicationBuilder(null);
builder.Logging.AddJsonConsole();
using var host = builder.Build();
host.Run();
输出结果如下:
使用我们自定义的 NewtonJsonConsole
时:
var builder = Host.CreateEmptyApplicationBuilder(null);
builder.Logging.AddNewtonJsonConsole();
using var host = builder.Build();
host.Run();
可以看到使用我们基于 NewtonsoftJson
自定义的 NewtonJsonConsole
时,Ctrl+C
不会再被 encode 了,可以看到原始的日志,就不用再看到一脸懵了
More
前面的实现仅供参考,仔细看的话会发现我们前面定义的 NewtonJsonConsole
并没有输出 OriginalFormat
,并且没有处理 scope 的信息,实际使用的话还需要根据需要进行修改,实现仅供参考
源码链接:https://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/DotNetCoreSample/NewtonJsonFormatter.cs