主控台記錄格式
在 .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
Program.cs 中呼叫 進行變更:
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 的記錄訊息。 記錄基礎結構本身已管理記錄訊息的序列化,因此若您要傳入已序列化的記錄訊息,則會進行雙重序列化,導致輸出格式不正確。
設定格式器的組態
先前範例已示範如何以程式設計方式登錄格式器。 這也可透過組態來進行。 若您更新 appsettings.json 檔案,而不是在 Program.cs 檔案中呼叫 ConfigureLogging
,請考慮先前的 Web 應用程式範例原始程式碼,則可取得相同的結果。 更新的 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.");
}
定義 ConsoleFormatter
的 CustomFormatter
子類別:
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"
節點指定"CustomTimePrefixingFormatter"
的"FormatterName"
,其對應至自訂格式器。"FormatterOptions"
節點會定義"CustomPrefix"
和"CustomSuffix"
,以及其他幾個衍生選項。
提示
系統會保留 $.Logging.Console.FormatterOptions
JSON 路徑,在使用 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 屬性,可用於啟用記錄中的色彩。
建立由 SimpleConsoleFormatterOptions
衍生的 CustomColorOptions
:
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();
}
當您執行應用程式時,記錄會在 FormatterOptions.ColorBehavior
為 Enabled
時以綠色顯示 CustomPrefix
訊息。
注意
當 LoggerColorBehavior 為 Disabled
時,記錄訊息不會解譯記錄訊息中的內嵌 ANSI 色彩代碼, 而是會輸出原始訊息。 例如,請考慮下列項目:
logger.LogInformation("Random log \x1B[42mwith green background\x1B[49m message");
如此會輸出逐字字串,且不會標示色彩。
Random log \x1B[42mwith green background\x1B[49m message