Udostępnij za pośrednictwem


Formatowanie dziennika konsoli

W .NET 5 dodano wsparcie dla niestandardowego formatowania dzienników konsoli w przestrzeni nazw Microsoft.Extensions.Logging.Console. Dostępne są trzy wstępnie zdefiniowane opcje formatowania: Simple, Systemdi Json.

Ważne

Wcześniej wyliczenie ConsoleLoggerFormat pozwalało na wybór żądanego formatu dziennika: czytelnego dla człowieka, który był Default, lub pojedynczy wiersz, znany jako Systemd. Nie można ich jednak dostosowywać i są teraz przestarzałe.

W tym artykule znajdziesz informacje o programach formatowania dzienników konsoli. Przykładowy kod źródłowy pokazuje, jak:

  • Zarejestruj nowego formatera
  • Wybierz zarejestrowany formater do użycia
  • Implementowanie niestandardowego formatatora

Wskazówka

Cały przykładowy kod źródłowy rejestrowania jest dostępny w przeglądarce Samples Browser do pobrania. Aby uzyskać więcej informacji, zobacz Przeglądanie przykładów kodu: Rejestrowanie na platformie .NET.

Zarejestruj moduł formatowania

Dostawca rejestrowaniaConsole ma kilka wstępnie zdefiniowanych formatów i uwidacznia możliwość tworzenia własnego niestandardowego formatnika. Aby zarejestrować dowolny z dostępnych formaterów, użyj odpowiedniej Add{Type}Console metody rozszerzenia:

Dostępne typy Metoda rejestrowania typu
ConsoleFormatterNames.Json ConsoleLoggerExtensions.AddJsonConsole
ConsoleFormatterNames.Simple ConsoleLoggerExtensions.AddSimpleConsole
ConsoleFormatterNames.Systemd ConsoleLoggerExtensions.AddSystemdConsole

Uproszczony

Aby użyć formatera konsoli, zarejestruj go za pomocą SimpleAddSimpleConsole.

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.");
}

W poprzednim przykładowym kodzie ConsoleFormatterNames.Simple źródłowym formater został zarejestrowany. Zapewnia ona dzienniki z możliwością nie tylko zawijania informacji, takich jak czas i poziom dziennika w każdym komunikacie dziennika, ale także umożliwia osadzanie kolorów ANSI i wcięcia komunikatów.

Po uruchomieniu tej przykładowej aplikacji komunikaty dziennika są formatowane, jak pokazano poniżej:

Przykładowe dzienniki konsoli zapisane przy użyciu prostego formatowania.

Systemd

ConsoleFormatterNames.Systemd Rejestrator konsoli:

  • Używa formatu poziomu logowania "Syslog" i poziomów ważności
  • Nie formatuje komunikatów przy użyciu kolorów
  • Zawsze rejestruje komunikaty w jednym wierszu

Jest to często przydatne w przypadku kontenerów, które często korzystają z logowania konsoli Systemd. W przypadku platformy .NET 5 rejestrator konsoli Simple umożliwia również kompaktową wersję, która zapisuje logi na jednej linii, oraz umożliwia wyłączenie kolorów, jak pokazano we wcześniejszym przykładzie.

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.");
}

W przykładzie są generowane dane wyjściowe podobne do następujących komunikatów dziennika:

Przykładowe dzienniki konsoli zapisane za pomocą programu Formatter Systemd.

Json

Aby zapisywać dzienniki w formacie JSON, Json używany jest program formatujący konsoli. Przykładowy kod źródłowy pokazuje, jak aplikacja ASP.NET Core może ją zarejestrować. Za pomocą szablonu webapp utwórz nową aplikację ASP.NET Core za pomocą polecenia dotnet new :

dotnet new webapp -o Console.ExampleFormatters.Json

Podczas uruchamiania aplikacji przy użyciu kodu szablonu zostanie wyświetlony domyślny format dziennika poniżej:

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

Domyślnie Simple formater dziennika konsoli jest wybierany z konfiguracją domyślną. Możesz to zmienić, wywołując AddJsonConsole w 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();

Alternatywnie można to skonfigurować przy użyciu konfiguracji rejestrowania, takiej jak ta znaleziona w pliku 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": "*"
}

Ponownie uruchom aplikację, po powyższej zmianie komunikat dziennika jest teraz sformatowany jako 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}"
  }
}

Wskazówka

Domyślnie Json program formatujący konsolę rejestruje każdy komunikat w jednym wierszu. Aby zwiększyć czytelność podczas konfiguracji formatera, ustaw JsonWriterOptions.Indented na true.

Ostrzeżenie

W przypadku korzystania z programu formatującego konsolę Json nie przekazuj komunikatów dziennika, które zostały już serializowane jako JSON. Sama infrastruktura rejestrowania zarządza już serializacją komunikatów dziennika, więc jeśli chcesz przekazać komunikat dziennika, który jest już serializowany — zostanie on zserializowany dwukrotnie, powodując źle sformułowane dane wyjściowe.

Ustaw formatowanie przy użyciu konfiguracji

W poprzednich przykładach pokazano, jak za pomocą kodu zarejestrować formater. Alternatywnie można to zrobić za pomocą konfiguracji. Rozważmy poprzedni przykładowy kod źródłowy aplikacji internetowej, jeśli zaktualizujesz plik appsettings.json zamiast wywoływać ConfigureLogging go w pliku Program.cs , możesz uzyskać ten sam wynik. Zaktualizowany appsettings.json plik skonfiguruje formater w następujący sposób:

{
    "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": "*"
}

Dwie wartości klucza, które należy ustawić, to "FormatterName" i "FormatterOptions". Jeśli formater z ustaloną wartością dla "FormatterName" jest już zarejestrowany, zostanie wybrany ten formater i jego właściwości można skonfigurować pod warunkiem, że są one podane jako klucz w węźle "FormatterOptions". Wstępnie zdefiniowane nazwy formaterów są zastrzeżone w obszarze ConsoleFormatterNames:

Implementowanie niestandardowego formatatora

Aby zaimplementować niestandardowy formatator, należy wykonać następujące kroki:

Utwórz metodę rozszerzenia, aby Ci to obsłużyła.

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);
}

Element CustomOptions jest zdefiniowany w następujący sposób:

using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.Custom;

public sealed class CustomOptions : ConsoleFormatterOptions
{
    public string? CustomPrefix { get; set; }
}

W poprzednim kodzie opcje są podklasą ConsoleFormatterOptions.

Interfejs AddConsoleFormatter API:

  • Rejestruje podklasę ConsoleFormatter
  • Obsługuje konfigurację:
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.");
}

Zdefiniuj podklasę CustomFormatter klasy 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();
}

Powyższy CustomFormatter.Write<TState> interfejs API określa, jaki tekst jest owinięty wokół każdego komunikatu dziennika. Standard ConsoleFormatter powinien być w stanie obejmować zakresy, znaczniki czasu i poziom ważności dzienników przynajmniej. Ponadto możesz zakodować kolory ANSI w komunikatach dziennika i podać również odpowiednie wcięcia. Implementacja CustomFormatter.Write<TState> nie posiada tych możliwości.

Aby uzyskać inspirację dotyczącą dalszego dostosowywania formatowania, zobacz istniejące implementacje w Microsoft.Extensions.Logging.Console przestrzeni nazw:

Opcje konfiguracji niestandardowej

Aby jeszcze bardziej dostosować rozszerzalność rejestrowania, klasę pochodną ConsoleFormatterOptions można skonfigurować z dowolnego dostawcy konfiguracji. Na przykład możesz użyć dostawcy konfiguracji JSON do zdefiniowania opcji niestandardowych. Najpierw zdefiniuj podklasę 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; }
}

Poprzednia klasa opcji formatowania konsoli definiuje dwie właściwości niestandardowe reprezentujące prefiks i sufiks. Następnie zdefiniuj plik appsettings.json , który skonfiguruje opcje formatowania konsoli.

{
    "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": "*"
}

W poprzednim pliku konfiguracji JSON:

  • Węzeł "Logging" definiuje element "Console".
  • Węzeł "Console" określa "FormatterName" z "CustomTimePrefixingFormatter", który odpowiada specjalnemu formatatorowi.
  • Węzeł "FormatterOptions" definiuje "CustomPrefix" i "CustomSuffix", a także kilka innych opcji pochodnych.

Wskazówka

Ścieżka $.Logging.Console.FormatterOptions JSON jest zarezerwowana i zostanie zamapowana na niestandardową ConsoleFormatterOptions przy dodawaniu przy użyciu metody rozszerzenia AddConsoleFormatter. Zapewnia to możliwość definiowania właściwości niestandardowych oprócz tych, które są dostępne.

Rozważ następujące 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();
}

W poprzedniej implementacji formatowania:

Aby użyć niestandardowych opcji konfiguracji, z niestandardowymi implementacjami formatowania, dodaj podczas wywoływania polecenia 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.");
}

Następujące dane wyjściowe konsoli są podobne do tego, czego można oczekiwać od użycia tego elementu CustomTimePrefixingFormatter.

|-<[ 15:03:15.6179 Hello World! ]>-|
|-<[ 15:03:15.6347 The .NET developer community happily welcomes you. ]>-|

Implementowanie niestandardowego formatowania kolorów

Aby prawidłowo włączyć funkcje kolorów w niestandardowym formatowaniu logów, możesz rozszerzyć element SimpleConsoleFormatterOptions, który ma właściwość SimpleConsoleFormatterOptions.ColorBehavior przydatną do włączania kolorów w dziennikach.

Utwórz element CustomColorOptions pochodzący z elementu SimpleConsoleFormatterOptions:

using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.Custom;

public class CustomColorOptions : SimpleConsoleFormatterOptions
{
    public string? CustomPrefix { get; set; }
}

Następnie napisz kilka metod rozszerzeń w TextWriterExtensions klasie, które umożliwiają wygodne osadzanie kolorów zakodowanych w ANSI w sformatowanych komunikatach dziennika.

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
        };
}

Niestandardowy formater kolorów, który obsługuje stosowanie kolorów niestandardowych, można zdefiniować w następujący sposób:

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();
}

Gdy uruchomisz aplikację, dzienniki wyświetlą komunikat CustomPrefix w kolorze zielonym, gdy FormatterOptions.ColorBehavior jest Enabled.

Uwaga / Notatka

Gdy LoggerColorBehavior jest Disabled, komunikaty dziennika nie interpretują osadzonych kodów kolorów ANSI. Zamiast tego emitują surowy komunikat. Rozważmy na przykład następujące kwestie:

logger.LogInformation("Random log \x1B[42mwith green background\x1B[49m message");

Spowoduje to wypisanie dosłownego ciągu, który nie jest pokolorowany.

Random log \x1B[42mwith green background\x1B[49m message

Zobacz także