Поделиться через


Форматирование журнала консоли

В .NET 5 поддержка пользовательского форматирования была добавлена в журналы консоли в Microsoft.Extensions.Logging.Console пространстве имен. Доступны три стандартных варианта форматирования: Simple, Systemdи Json.

Это важно

Ранее тип перечисления ConsoleLoggerFormat позволял выбрать нужный формат журнала: человеко-читаемый, который был Default, или одной строкой, иначе называемый Systemd. Однако они не были настраиваемыми и теперь считаются устаревшими.

В этой статье вы узнаете о модулях форматирования журнала консоли. В примере исходного кода показано, как:

  • Регистрация нового модуля форматирования
  • Выбор зарегистрированного средства форматирования для использования
  • Создание пользовательского форматировщика
    • Обновление конфигурации с помощью IOptionsMonitor<TOptions>
    • Включение настраиваемого форматирования цветов

Подсказка

Весь пример исходного кода для ведения журнала доступен для загрузки в Обозревателе примеров. Дополнительные сведения см. в разделе Обзор примеров кода: ведение журнала в .NET.

Форматтер регистрации

ПоставщикConsole ведения журнала имеет несколько предопределенных средств форматирования и предоставляет возможность создавать собственный настраиваемый форматировщик. Чтобы зарегистрировать любой из доступных форматировщиков, используйте соответствующий Add{Type}Console метод расширения:

Доступные типы Метод регистрации типа
ConsoleFormatterNames.Json ConsoleLoggerExtensions.AddJsonConsole
ConsoleFormatterNames.Simple ConsoleLoggerExtensions.AddSimpleConsole
ConsoleFormatterNames.Systemd ConsoleLoggerExtensions.AddSystemdConsole

Простой

Чтобы использовать 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.");
}

В примере создаются выходные данные, аналогичные следующим сообщениям журнала:

Примеры журналов консоли, написанные с помощью systemd formatter.

Json

Для записи журналов в формате Json JSON используется модуль форматирования консоли. В примере исходного кода показано, как может зарегистрировать приложение ASP.NET Core. webapp С помощью шаблона создайте новое приложение ASP.NET Core с помощью команды dotnet new:

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 вместо вызова ConfigureLogging в файле Program.cs , вы можете получить тот же результат. Обновленный 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разделе :

Реализация пользовательского модуля форматирования

Для реализации пользовательского модуля форматирования необходимо выполнить следующие действия.

Создайте метод расширения для обработки этой функции:

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.

The AddConsoleFormatter API:

  • Регистрирует подкласс ConsoleFormatter
  • Управление конфигурацией:
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.");
}

Определите подкласс CustomFormatterConsoleFormatter.

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", а также несколько других производных параметров.

Подсказка

$.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, которое может быть полезно для включения цветов в журнале.

Создайте производный 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

См. также