Formatowanie dziennika konsoli

Na platformie .NET 5 dodano obsługę niestandardowego Microsoft.Extensions.Logging.Console formatowania do dzienników konsoli w przestrzeni nazw. Dostępne są trzy wstępnie zdefiniowane opcje formatowania: Simple, Systemdi Json.

Ważne

Wcześniej wyliczenie ConsoleLoggerFormat dozwolone do wybrania żądanego formatu dziennika, czytelnego dla człowieka, który był wierszem Default, lub pojedynczym wierszem, który jest również 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:

  • Rejestrowanie nowego formatnika
  • Wybierz zarejestrowany formater do użycia
  • Implementowanie niestandardowego formatatora

Napiwek

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 formater

Dostawca rejestrowania Console 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ć programu formatującego konsolę, zarejestruj go za Simple pomocą AddSimpleConsolepolecenia :

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 i ważności na poziomie dziennika "Syslog"
  • Nie formatuje komunikatów z kolorami
  • Zawsze rejestruje komunikaty w jednym wierszu

Jest to często przydatne w przypadku kontenerów, które często korzystają z rejestrowania Systemd konsoli. W przypadku platformy .NET 5 Simple rejestrator konsoli włącza również kompaktową wersję, która rejestruje się w jednym wierszu, a także 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.

Dane w formacie 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ę platformy 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 polecenie 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}"
  }
}

Napiwek

Domyślnie Json program formatujący konsolę rejestruje każdy komunikat w jednym wierszu. Aby zwiększyć czytelność podczas konfigurowania programu formatującego, ustaw wartość JsonWriterOptions.Indentedtrue.

Uwaga

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.

Ustawianie formatowania przy użyciu konfiguracji

W poprzednich przykładach pokazano, jak programowo 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 element formatujący z ustawioną "FormatterName" wartością dla jest już zarejestrowany, jest wybrany ten formater, a jego właściwości można skonfigurować tak długo, jak 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 obsłużyć tę metodę:

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 opakowować zakresy, sygnatury czasowe i poziom ważności dzienników co najmniej. Ponadto możesz zakodować kolory ANSI w komunikatach dziennika i podać również odpowiednie wcięcia. Implementacja CustomFormatter.Write<TState> tych możliwości nie jest dostępna.

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 element "FormatterName""CustomTimePrefixingFormatter", który jest mapowy na niestandardowy formatator.
  • Węzeł "FormatterOptions" definiuje "CustomPrefix"wartości , i "CustomSuffix", a także kilka innych opcji pochodnych.

Napiwek

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

Rozważ następujące kwestie 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 module formatowania rejestrowania, można rozszerzyć SimpleConsoleFormatterOptions element , ponieważ ma SimpleConsoleFormatterOptions.ColorBehavior właściwość, która może być przydatna 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 kodowanych przez usługę 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();
}

Po uruchomieniu aplikacji dzienniki będą wyświetlać CustomPrefix komunikat w kolorze zielonym, gdy FormatterOptions.ColorBehavior ma wartość Enabled.

Uwaga

Gdy LoggerColorBehavior jest Disabledto , komunikaty dziennika nie interpretują osadzonych kodów kolorów ANSI w komunikatach dziennika. Zamiast tego wyjdą nieprzetworzonego komunikatu. Rozważmy na przykład następujące kwestie:

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

Spowoduje to wyjście ciągu dosłownych i nie jest kolorowane.

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

Zobacz też