在 .NET 5 中,已將自定義格式的支援新增至命名空間中的 Microsoft.Extensions.Logging.Console 控制台記錄。 有三個預先定義的格式化選項可用: Simple、 Systemd和 Json。
這很重要
先前,ConsoleLoggerFormat 列舉允許選擇所需的日誌格式,可以是人類可讀的格式 Default,或也被稱為 Systemd 的單行格式。 不過,這些 是無法 自定義的,現在已被取代。
在本文中,您將瞭解主控台記錄格式器。 範例原始程式碼示範如何:
- 註冊新的格式器
- 選擇要使用的已註冊格式化工具
- 透過程式代碼或 設定
- 實作自定義格式器
- 通過IOptionsMonitor<TOptions>更新組態
- 啟用自訂色彩格式設定
小提示
所有記錄範例原始程式碼都可在範例瀏覽器中下載。 如需詳細資訊,請參閱 瀏覽程式碼範例: 在 .NET 中記錄。
暫存器格式器
記錄Console提供者有數個預先定義的格式器,並提供工具讓您撰寫自己的自定義格式器。 若要註冊任何可用的格式器,請使用對應的 Add{Type}Console 擴充方法:
簡單
若要使用 Simple 主控台格式器,請向 AddSimpleConsole註冊它:
using Microsoft.Extensions.Logging;
using ILoggerFactory loggerFactory =
LoggerFactory.Create(builder =>
builder.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
options.SingleLine = true;
options.TimestampFormat = "HH:mm:ss ";
}));
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
using (logger.BeginScope("[scope is enabled]"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("Logs contain timestamp and log level.");
logger.LogInformation("Each log message is fit in a single line.");
}
在前述的範例程式碼中,已註冊了ConsoleFormatterNames.Simple格式器。 它提供記錄的功能不僅能包裝每個記錄訊息中的時間和記錄層級等資訊,還能允許 ANSI 色彩內嵌和縮排訊息。
執行此範例應用程式時,記錄訊息的格式如下:
Systemd
主控台 ConsoleFormatterNames.Systemd 記錄器:
- 使用 「Syslog」 記錄層級格式和嚴重性
- 不會使用色彩格式化訊息
- 訊息一律記錄在單行中
這通常對於經常使用主控台記錄的 Systemd 容器特別有用。 使用 .NET 5 時, Simple 控制台記錄器也會啟用精簡版本,以單行記錄,並允許停用色彩,如先前範例所示。
using Microsoft.Extensions.Logging;
using ILoggerFactory loggerFactory =
LoggerFactory.Create(builder =>
builder.AddSystemdConsole(options =>
{
options.IncludeScopes = true;
options.TimestampFormat = "HH:mm:ss ";
}));
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
using (logger.BeginScope("[scope is enabled]"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("Logs contain timestamp and log level.");
logger.LogInformation("Systemd console logs never provide color options.");
logger.LogInformation("Systemd console logs always appear in a single line.");
}
此範例會產生類似下列記錄訊息的輸出:
Json
若要以 JSON 格式寫入記錄,則會 Json 使用主控台格式器。 範例原始碼示範 ASP.NET Core 應用程式如何註冊它。
webapp使用範本,使用 dotnet new 命令建立新的 ASP.NET Core 應用程式:
dotnet new webapp -o Console.ExampleFormatters.Json
執行應用程式時,使用範本程式碼,您會取得下列預設記錄格式:
info: Console.ExampleFormatters.Json.Startup[0]
Hello .NET friends!
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: .\snippets\logging\console-formatter-json
預設情況下,會選擇使用預設組態的Simple主控台日誌格式器。 您可以在 AddJsonConsole 中呼叫 來更改此項目。
using System.Text.Json;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddJsonConsole(options =>
{
options.IncludeScopes = false;
options.TimestampFormat = "HH:mm:ss ";
options.JsonWriterOptions = new JsonWriterOptions
{
Indented = true
};
});
using IHost host = builder.Build();
var logger =
host.Services
.GetRequiredService<ILoggerFactory>()
.CreateLogger<Program>();
logger.LogInformation("Hello .NET friends!");
await host.RunAsync();
或者,您也可以透過記錄組態來進行設定,例如在 appsettings.json 檔案中找到的組態設定:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"FormatterName": "json",
"FormatterOptions": {
"SingleLine": true,
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss ",
"UseUtcTimestamp": true,
"JsonWriterOptions": {
"Indented": true
}
}
}
},
"AllowedHosts": "*"
}
再次執行應用程式,使用上述變更,記錄訊息現在會格式化為 JSON:
{
"Timestamp": "02:28:19 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Console.ExampleFormatters.Json.Startup",
"Message": "Hello .NET friends!",
"State": {
"Message": "Hello .NET friends!",
"{OriginalFormat}": "Hello .NET friends!"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 14,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Now listening on: https://localhost:5001",
"State": {
"Message": "Now listening on: https://localhost:5001",
"address": "https://localhost:5001",
"{OriginalFormat}": "Now listening on: {address}"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 14,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Now listening on: http://localhost:5000",
"State": {
"Message": "Now listening on: http://localhost:5000",
"address": "http://localhost:5000",
"{OriginalFormat}": "Now listening on: {address}"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Application started. Press Ctrl\u002BC to shut down.",
"State": {
"Message": "Application started. Press Ctrl\u002BC to shut down.",
"{OriginalFormat}": "Application started. Press Ctrl\u002BC to shut down."
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Hosting environment: Development",
"State": {
"Message": "Hosting environment: Development",
"envName": "Development",
"{OriginalFormat}": "Hosting environment: {envName}"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Content root path: .\\snippets\\logging\\console-formatter-json",
"State": {
"Message": "Content root path: .\\snippets\\logging\\console-formatter-json",
"contentRoot": ".\\snippets\\logging\\console-formatter-json",
"{OriginalFormat}": "Content root path: {contentRoot}"
}
}
小提示
主控台 Json 格式器預設會記錄單行中的每個訊息。 若要讓它在設定格式器時更容易閱讀,請將 設定 JsonWriterOptions.Indented 為 true。
謹慎
使用 Json 主控台格式器時,請勿傳入已串行化為 JSON 的記錄訊息。 記錄基礎結構本身已經管理記錄訊息的串行化,因此,如果您要傳入已串行化的記錄訊息,則會進行雙重串行化,因而造成格式不正確的輸出。
透過配置設定格式器
先前的範例示範如何以程序設計方式註冊格式器。 或者,這可以透過 設定來完成。 請考慮先前的 Web 應用程式範例原始程式碼,如果您更新 appsettings.json 檔案,而不是在ConfigureLogging檔案中呼叫,您可以取得相同的結果。 更新後的 appsettings.json 檔案會將格式器設定如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"FormatterName": "json",
"FormatterOptions": {
"SingleLine": true,
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss ",
"UseUtcTimestamp": true,
"JsonWriterOptions": {
"Indented": true
}
}
}
},
"AllowedHosts": "*"
}
需要設定的兩個索引鍵值是 "FormatterName" 和 "FormatterOptions"。 如果已為 設定 "FormatterName" 值的格式器已註冊,則會選取該格式子,只要它們以節點內的 "FormatterOptions" 索引鍵形式提供,就可以設定其屬性。 定義好的格式器名稱會保留在 ConsoleFormatterNames 下:
實作自定義格式器
若要實作自定義格式器,您需要:
- 建立ConsoleFormatter的子類別,這代表您的自定義格式器
- 註冊您的自訂格式化器
建立擴充方法,為您處理此問題:
using Microsoft.Extensions.Logging;
namespace Console.ExampleFormatters.Custom;
public static class ConsoleLoggerExtensions
{
public static ILoggingBuilder AddCustomFormatter(
this ILoggingBuilder builder,
Action<CustomOptions> configure) =>
builder.AddConsole(options => options.FormatterName = "customName")
.AddConsoleFormatter<CustomFormatter, CustomOptions>(configure);
}
CustomOptions的定義如下:
using Microsoft.Extensions.Logging.Console;
namespace Console.ExampleFormatters.Custom;
public sealed class CustomOptions : ConsoleFormatterOptions
{
public string? CustomPrefix { get; set; }
}
在上述程式代碼中,選項是 的 ConsoleFormatterOptions子類別。
AddConsoleFormatter API:
- 註冊 子類別
ConsoleFormatter - 處理組態:
- 使用變更令牌,根據 選項模式和 IOptionsMonitor 介面同步處理更新
using Console.ExampleFormatters.Custom;
using Microsoft.Extensions.Logging;
using ILoggerFactory loggerFactory =
LoggerFactory.Create(builder =>
builder.AddCustomFormatter(options =>
options.CustomPrefix = " ~~~~~ "));
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
using (logger.BeginScope("TODO: Add logic to enable scopes"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("TODO: Add logic to enable timestamp and log level info.");
}
定義CustomFormatter的一個ConsoleFormatter子類別:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
namespace Console.ExampleFormatters.Custom;
public sealed class CustomFormatter : ConsoleFormatter, IDisposable
{
private readonly IDisposable? _optionsReloadToken;
private CustomOptions _formatterOptions;
public CustomFormatter(IOptionsMonitor<CustomOptions> options)
// Case insensitive
: base("customName") =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);
private void ReloadLoggerOptions(CustomOptions options) =>
_formatterOptions = options;
public override void Write<TState>(
in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter textWriter)
{
string? message =
logEntry.Formatter?.Invoke(
logEntry.State, logEntry.Exception);
if (message is null)
{
return;
}
CustomLogicGoesHere(textWriter);
textWriter.WriteLine(message);
}
private void CustomLogicGoesHere(TextWriter textWriter)
{
textWriter.Write(_formatterOptions.CustomPrefix);
}
public void Dispose() => _optionsReloadToken?.Dispose();
}
上述 CustomFormatter.Write<TState> API 會決定包裹於每個記錄訊息周圍的文字。 標準ConsoleFormatter應該至少能夠包覆日誌的範圍、時間戳記和嚴重性層級。 此外,您也可以在記錄訊息中編碼 ANSI 色彩,並提供所需的縮排。
CustomFormatter.Write<TState> 的實施缺乏這些功能。
如需進一步自定義格式的靈感,請參閱 命名空間中的 Microsoft.Extensions.Logging.Console 現有實作:
自訂組態選項
若要自定義日誌擴充性,您可以從任何ConsoleFormatterOptions設定您的衍生類別。 例如,您可以使用 JSON 組態提供者 來定義您的自訂選項。 首先定義您的 ConsoleFormatterOptions 子類別。
using Microsoft.Extensions.Logging.Console;
namespace Console.ExampleFormatters.CustomWithConfig;
public sealed class CustomWrappingConsoleFormatterOptions : ConsoleFormatterOptions
{
public string? CustomPrefix { get; set; }
public string? CustomSuffix { get; set; }
}
上述主控台格式子選項類別會定義兩個自定義屬性,代表前置詞和後綴。 接下來,定義 appsettings.json 檔案,以設定主控台格式器選項。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"FormatterName": "CustomTimePrefixingFormatter",
"FormatterOptions": {
"CustomPrefix": "|-<[",
"CustomSuffix": "]>-|",
"SingleLine": true,
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss.ffff ",
"UseUtcTimestamp": true,
"JsonWriterOptions": {
"Indented": true
}
}
}
},
"AllowedHosts": "*"
}
在上述 JSON 設定檔中:
-
"Logging"節點 定義"Console"。 - 節點
"Console"指定了一個"FormatterName"的"CustomTimePrefixingFormatter",並映射到自定義格式器。 - 節點
"FormatterOptions"定義了"CustomPrefix"和"CustomSuffix",以及一些其他衍生選項。
小提示
JSON $.Logging.Console.FormatterOptions 路徑已保留,且會在使用ConsoleFormatterOptions擴充方法新增時對應至自定義AddConsoleFormatter。 除了可用的屬性之外,這可讓您定義自定義屬性。
請考慮下列事項 CustomDatePrefixingFormatter:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
namespace Console.ExampleFormatters.CustomWithConfig;
public sealed class CustomTimePrefixingFormatter : ConsoleFormatter, IDisposable
{
private readonly IDisposable? _optionsReloadToken;
private CustomWrappingConsoleFormatterOptions _formatterOptions;
public CustomTimePrefixingFormatter(
IOptionsMonitor<CustomWrappingConsoleFormatterOptions> options)
// Case insensitive
: base(nameof(CustomTimePrefixingFormatter))
{
_optionsReloadToken = options.OnChange(ReloadLoggerOptions);
_formatterOptions = options.CurrentValue;
}
private void ReloadLoggerOptions(CustomWrappingConsoleFormatterOptions options) =>
_formatterOptions = options;
public override void Write<TState>(
in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter textWriter)
{
string message =
logEntry.Formatter(
logEntry.State, logEntry.Exception);
if (message == null)
{
return;
}
WritePrefix(textWriter);
textWriter.Write(message);
WriteSuffix(textWriter);
}
private void WritePrefix(TextWriter textWriter)
{
DateTime now = _formatterOptions.UseUtcTimestamp
? DateTime.UtcNow
: DateTime.Now;
textWriter.Write($"""
{_formatterOptions.CustomPrefix} {now.ToString(_formatterOptions.TimestampFormat)}
""");
}
private void WriteSuffix(TextWriter textWriter) =>
textWriter.WriteLine($" {_formatterOptions.CustomSuffix}");
public void Dispose() => _optionsReloadToken?.Dispose();
}
在前述格式器實作中:
-
CustomWrappingConsoleFormatterOptions會監視變更,並據以更新。 - 寫入的訊息會以已設定的前置詞和後綴包裝。
- 在前置詞之後、訊息之前,會使用已設定的 ConsoleFormatterOptions.UseUtcTimestamp 和 ConsoleFormatterOptions.TimestampFormat 值來新增時間戳。
若要使用自訂組態選項,搭配自訂格式器實作,請在呼叫 ConfigureLogging(IHostBuilder, Action<HostBuilderContext,ILoggingBuilder>)時新增 。
using Console.ExampleFormatters.CustomWithConfig;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole()
.AddConsoleFormatter<
CustomTimePrefixingFormatter, CustomWrappingConsoleFormatterOptions>();
using IHost host = builder.Build();
ILoggerFactory loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
using (logger.BeginScope("Logging scope"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("The .NET developer community happily welcomes you.");
}
以下主控台輸出應類似於您使用這個 CustomTimePrefixingFormatter 時可能會看到的結果。
|-<[ 15:03:15.6179 Hello World! ]>-|
|-<[ 15:03:15.6347 The .NET developer community happily welcomes you. ]>-|
實作自定義色彩格式設定
若要在自訂記錄格式器中正確啟用色彩功能,您可以擴充 SimpleConsoleFormatterOptions ,因為它具有 SimpleConsoleFormatterOptions.ColorBehavior 屬性,可用於啟用記錄中的色彩。
創建一個 CustomColorOptions,它衍生自 SimpleConsoleFormatterOptions。
using Microsoft.Extensions.Logging.Console;
namespace Console.ExampleFormatters.Custom;
public class CustomColorOptions : SimpleConsoleFormatterOptions
{
public string? CustomPrefix { get; set; }
}
接下來,在類別中 TextWriterExtensions 撰寫一些擴充方法,以方便在格式化的記錄訊息中內嵌 ANSI 編碼色彩:
namespace Console.ExampleFormatters.Custom;
public static class TextWriterExtensions
{
const string DefaultForegroundColor = "\x1B[39m\x1B[22m";
const string DefaultBackgroundColor = "\x1B[49m";
public static void WriteWithColor(
this TextWriter textWriter,
string message,
ConsoleColor? background,
ConsoleColor? foreground)
{
// Order:
// 1. background color
// 2. foreground color
// 3. message
// 4. reset foreground color
// 5. reset background color
var backgroundColor = background.HasValue ? GetBackgroundColorEscapeCode(background.Value) : null;
var foregroundColor = foreground.HasValue ? GetForegroundColorEscapeCode(foreground.Value) : null;
if (backgroundColor != null)
{
textWriter.Write(backgroundColor);
}
if (foregroundColor != null)
{
textWriter.Write(foregroundColor);
}
textWriter.WriteLine(message);
if (foregroundColor != null)
{
textWriter.Write(DefaultForegroundColor);
}
if (backgroundColor != null)
{
textWriter.Write(DefaultBackgroundColor);
}
}
static string GetForegroundColorEscapeCode(ConsoleColor color) =>
color switch
{
ConsoleColor.Black => "\x1B[30m",
ConsoleColor.DarkRed => "\x1B[31m",
ConsoleColor.DarkGreen => "\x1B[32m",
ConsoleColor.DarkYellow => "\x1B[33m",
ConsoleColor.DarkBlue => "\x1B[34m",
ConsoleColor.DarkMagenta => "\x1B[35m",
ConsoleColor.DarkCyan => "\x1B[36m",
ConsoleColor.Gray => "\x1B[37m",
ConsoleColor.Red => "\x1B[1m\x1B[31m",
ConsoleColor.Green => "\x1B[1m\x1B[32m",
ConsoleColor.Yellow => "\x1B[1m\x1B[33m",
ConsoleColor.Blue => "\x1B[1m\x1B[34m",
ConsoleColor.Magenta => "\x1B[1m\x1B[35m",
ConsoleColor.Cyan => "\x1B[1m\x1B[36m",
ConsoleColor.White => "\x1B[1m\x1B[37m",
_ => DefaultForegroundColor
};
static string GetBackgroundColorEscapeCode(ConsoleColor color) =>
color switch
{
ConsoleColor.Black => "\x1B[40m",
ConsoleColor.DarkRed => "\x1B[41m",
ConsoleColor.DarkGreen => "\x1B[42m",
ConsoleColor.DarkYellow => "\x1B[43m",
ConsoleColor.DarkBlue => "\x1B[44m",
ConsoleColor.DarkMagenta => "\x1B[45m",
ConsoleColor.DarkCyan => "\x1B[46m",
ConsoleColor.Gray => "\x1B[47m",
_ => DefaultBackgroundColor
};
}
可以定義如下自訂色彩格式器,來處理和應用自訂顏色:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
namespace Console.ExampleFormatters.Custom;
public sealed class CustomColorFormatter : ConsoleFormatter, IDisposable
{
private readonly IDisposable? _optionsReloadToken;
private CustomColorOptions _formatterOptions;
private bool ConsoleColorFormattingEnabled =>
_formatterOptions.ColorBehavior == LoggerColorBehavior.Enabled ||
_formatterOptions.ColorBehavior == LoggerColorBehavior.Default &&
System.Console.IsOutputRedirected == false;
public CustomColorFormatter(IOptionsMonitor<CustomColorOptions> options)
// Case insensitive
: base("customName") =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);
private void ReloadLoggerOptions(CustomColorOptions options) =>
_formatterOptions = options;
public override void Write<TState>(
in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter textWriter)
{
if (logEntry.Exception is null)
{
return;
}
string? message =
logEntry.Formatter?.Invoke(
logEntry.State, logEntry.Exception);
if (message is null)
{
return;
}
CustomLogicGoesHere(textWriter);
textWriter.WriteLine(message);
}
private void CustomLogicGoesHere(TextWriter textWriter)
{
if (ConsoleColorFormattingEnabled)
{
textWriter.WriteWithColor(
_formatterOptions.CustomPrefix ?? string.Empty,
ConsoleColor.Black,
ConsoleColor.Green);
}
else
{
textWriter.Write(_formatterOptions.CustomPrefix);
}
}
public void Dispose() => _optionsReloadToken?.Dispose();
}
當您執行應用程式時,當CustomPrefix是FormatterOptions.ColorBehavior時,記錄會以綠色顯示Enabled訊息。
備註
當LoggerColorBehavior是Disabled時,不會解譯記錄訊息中的內嵌 ANSI 色彩代碼。 相反地,他們會輸出原始訊息。 例如,請考慮下列事項:
logger.LogInformation("Random log \x1B[42mwith green background\x1B[49m message");
這會輸出逐字字串,而且 不會 標示色彩。
Random log \x1B[42mwith green background\x1B[49m message