Реализация пользовательского поставщика ведения журнала в .NET

Существует множество поставщиков ведения журнала, доступных для общих задач ведения журнала. Вам может потребоваться реализовать свой объект ILoggerProvider, если ни один из доступных поставщиков не соответствует потребностям вашего приложения. Из этой статьи вы узнаете, как реализовать настраиваемый поставщик ведения журнала, который можно использовать для выделения цветом журналов в консоли.

Совет

В репозитории Docs Github доступен пример исходного кода поставщика ведения журнала. Дополнительные сведения см. в разделе GitHub: Документация .NET — настраиваемое ведение журнала консоли.

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

В этом примере создаются записи консоли разного цвета в соответствии с уровнем ведения журнала и идентификатором события с использованием следующего типа конфигурации:

using Microsoft.Extensions.Logging;

public sealed class ColorConsoleLoggerConfiguration
{
    public int EventId { get; set; }

    public Dictionary<LogLevel, ConsoleColor> LogLevelToColorMap { get; set; } = new()
    {
        [LogLevel.Information] = ConsoleColor.Green
    };
}

Приведенный выше код задает уровень по умолчанию Information и цвет Green, а EventId неявно равен 0.

Создание пользовательского средства ведения журнала

В качестве имени категории реализации ILogger, как правило, используется источник журнала. Например, тип, в котором создается средство ведения журнала:

using Microsoft.Extensions.Logging;

public sealed class ColorConsoleLogger(
    string name,
    Func<ColorConsoleLoggerConfiguration> getCurrentConfig) : ILogger
{
    public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!;

    public bool IsEnabled(LogLevel logLevel) =>
        getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel);

    public void Log<TState>(
        LogLevel logLevel,
        EventId eventId,
        TState state,
        Exception? exception,
        Func<TState, Exception?, string> formatter)
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        ColorConsoleLoggerConfiguration config = getCurrentConfig();
        if (config.EventId == 0 || config.EventId == eventId.Id)
        {
            ConsoleColor originalColor = Console.ForegroundColor;

            Console.ForegroundColor = config.LogLevelToColorMap[logLevel];
            Console.WriteLine($"[{eventId.Id,2}: {logLevel,-12}]");
            
            Console.ForegroundColor = originalColor;
            Console.Write($"     {name} - ");

            Console.ForegroundColor = config.LogLevelToColorMap[logLevel];
            Console.Write($"{formatter(state, exception)}");
            
            Console.ForegroundColor = originalColor;
            Console.WriteLine();
        }
    }
}

Предыдущий код:

  • создает экземпляр средства ведения журнала по имени категории;
  • проверяет _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel) в IsEnabled, благодаря чему каждому logLevel соответствует уникальное средство ведения журнала. В этой реализации каждому уровню журнала требуется явная запись конфигурации для журнала.

Рекомендуется ILogger.IsEnabled вызывать реализацию, ILogger.Log так как Log ее можно вызывать любым потребителем, и нет никаких гарантий, что ранее проверка. Метод IsEnabled должен быть очень быстрым в большинстве реализаций.

TState state,
Exception? exception,

Средство ведения журнала создается с name помощью экземпляра и Func<ColorConsoleLoggerConfiguration>, который возвращает текущую конфигурацию, это обрабатывает обновления значений конфигурации, отслеживаемых с помощью обратного IOptionsMonitor<TOptions>.OnChange вызова.

Внимание

Реализация ILogger.Log проверяет, задано ли значение config.EventId. Если значение config.EventId не установлено или если оно совпадает с logEntry.EventId, для средства ведения журнала применяется цветовая схема.

Пользовательский поставщик ведения журнала

Объект ILoggerProvider отвечает за создание экземпляров средства ведения журналов. Создавать экземпляры средства ведения журнала для каждой категории не требуется, но это может быть полезно для таких средств, как NLog или log4net. Такая стратегия позволяет выбрать разные выходные целевые объекты для записи в журнал для каждой категории, как показано в следующем примере:

using System.Collections.Concurrent;
using System.Runtime.Versioning;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

[UnsupportedOSPlatform("browser")]
[ProviderAlias("ColorConsole")]
public sealed class ColorConsoleLoggerProvider : ILoggerProvider
{
    private readonly IDisposable? _onChangeToken;
    private ColorConsoleLoggerConfiguration _currentConfig;
    private readonly ConcurrentDictionary<string, ColorConsoleLogger> _loggers =
        new(StringComparer.OrdinalIgnoreCase);

    public ColorConsoleLoggerProvider(
        IOptionsMonitor<ColorConsoleLoggerConfiguration> config)
    {
        _currentConfig = config.CurrentValue;
        _onChangeToken = config.OnChange(updatedConfig => _currentConfig = updatedConfig);
    }

    public ILogger CreateLogger(string categoryName) =>
        _loggers.GetOrAdd(categoryName, name => new ColorConsoleLogger(name, GetCurrentConfig));

    private ColorConsoleLoggerConfiguration GetCurrentConfig() => _currentConfig;

    public void Dispose()
    {
        _loggers.Clear();
        _onChangeToken?.Dispose();
    }
}

В приведенном выше коде CreateLogger создает один экземпляр ColorConsoleLogger для каждого имени категории и сохраняет его в ConcurrentDictionary<TKey,TValue>. Кроме того, интерфейс IOptionsMonitor<TOptions> требуется для обновления изменений в базовом объекте ColorConsoleLoggerConfiguration.

Чтобы управлять конфигурацией, ColorConsoleLoggerвы определяете псевдоним для своего поставщика:

[UnsupportedOSPlatform("browser")]
[ProviderAlias("ColorConsole")]
public sealed class ColorConsoleLoggerProvider : ILoggerProvider

Класс ColorConsoleLoggerProvider определяет два атрибута область класса:

  • UnsupportedOSPlatformAttributeColorConsoleLogger: тип не поддерживается в "browser".
  • ProviderAliasAttribute: разделы конфигурации могут определять параметры с помощью "ColorConsole" ключа.

Конфигурацию можно указать с любым допустимым поставщиком конфигурации. Рассмотрите следующий файл appsettings.json:

{
    "Logging": {
        "ColorConsole": {
            "LogLevelToColorMap": {
                "Information": "DarkGreen",
                "Warning": "Cyan",
                "Error": "Red"
            }
        }
    }
}

Это настраивает уровни журнала на следующие значения:

Для Information уровня журнала задано DarkGreenзначение , которое переопределяет значение по умолчанию, заданное в объекте ColorConsoleLoggerConfiguration .

Использование и регистрация пользовательского средства ведения журнала

По соглашению регистрация служб для внедрения зависимостей происходит как часть подпрограммы запуска приложения. Регистрация выполняется в классе Program или может быть делегирована классу Startup. В этом примере вы выполняете регистрацию напрямую из файла Program.cs.

Чтобы добавить пользовательского поставщика ведения журнала и соответствующее средство ведения журнала, добавьте ILoggerProvider с помощью ILoggingBuilder из HostingHostBuilderExtensions.ConfigureLogging(IHostBuilder, Action<ILoggingBuilder>):

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddColorConsoleLogger(configuration =>
{
    // Replace warning value from appsettings.json of "Cyan"
    configuration.LogLevelToColorMap[LogLevel.Warning] = ConsoleColor.DarkCyan;
    // Replace warning value from appsettings.json of "Red"
    configuration.LogLevelToColorMap[LogLevel.Error] = ConsoleColor.DarkRed;
});

using IHost host = builder.Build();

var logger = host.Services.GetRequiredService<ILogger<Program>>();

logger.LogDebug(1, "Does this line get hit?");    // Not logged
logger.LogInformation(3, "Nothing to see here."); // Logs in ConsoleColor.DarkGreen
logger.LogWarning(5, "Warning... that was odd."); // Logs in ConsoleColor.DarkCyan
logger.LogError(7, "Oops, there was an error.");  // Logs in ConsoleColor.DarkRed
logger.LogTrace(5, "== 120.");                    // Not logged

await host.RunAsync();

ILoggingBuilder создает один или несколько экземпляров ILogger. Экземпляры ILogger используются платформой для записи данных в журнал.

Конфигурация из файла appsettings.json переопределяет следующие значения:

По соглашению методы расширения в ILoggingBuilder используются для регистрации настраиваемого поставщика:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;

public static class ColorConsoleLoggerExtensions
{
    public static ILoggingBuilder AddColorConsoleLogger(
        this ILoggingBuilder builder)
    {
        builder.AddConfiguration();

        builder.Services.TryAddEnumerable(
            ServiceDescriptor.Singleton<ILoggerProvider, ColorConsoleLoggerProvider>());

        LoggerProviderOptions.RegisterProviderOptions
            <ColorConsoleLoggerConfiguration, ColorConsoleLoggerProvider>(builder.Services);

        return builder;
    }

    public static ILoggingBuilder AddColorConsoleLogger(
        this ILoggingBuilder builder,
        Action<ColorConsoleLoggerConfiguration> configure)
    {
        builder.AddColorConsoleLogger();
        builder.Services.Configure(configure);

        return builder;
    }
}

При запуске этого простого приложения в окне консоли будет выведен цветной текст, как показано на следующем рисунке:

Color console logger sample output

См. также