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


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

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

Подсказка

Пример исходного кода кастомного провайдера логов доступен в GitHub-репозитории Docs.

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

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

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

Каждый экземпляр логгера создается путем передачи имени категории, которое обычно является типом, в котором создаётся логгер. Метод IsEnabled проверяет getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel), чтобы определить, включен ли запрошенный уровень журнала в конфигурационном словаре уровней журналов.

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

if (!IsEnabled(logLevel))
{
    return;
}

Журнал создается с помощью name и Func<ColorConsoleLoggerConfiguration>, который возвращает текущую конфигурацию.

Это важно

Реализация 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(String) создает один экземпляр ColorConsoleLogger для имени категории и сохраняет его в ConcurrentDictionary<TKey,TValue>.

Класс ColorConsoleLoggerProvider украшен двумя атрибутами:

[UnsupportedOSPlatform("browser")]
[ProviderAlias("ColorConsole")]
public sealed class ColorConsoleLoggerProvider : ILoggerProvider
  • UnsupportedOSPlatformAttribute: тип ColorConsoleLogger не поддерживается в "browser".
  • ProviderAliasAttribute. Разделы конфигурации могут определять параметры с помощью ключа "ColorConsole".

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

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

Файл appsettings.json указывает, что цвет, ассоциированный с Information уровнем журнала DarkGreen, переопределяет значение по умолчанию, заданное в объекте ColorConsoleLoggerConfiguration.

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

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

Чтобы добавить настраиваемый поставщик ведения журнала и соответствующий средство ведения журнала, добавьте его ILoggerProvider путем вызова пользовательского метода расширения в AddColorConsoleLoggerсвойстве ILoggingBuilderIHostApplicationBuilder.Logging :

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 value of "Cyan" from appsettings.json.
    configuration.LogLevelToColorMap[LogLevel.Warning]
        = ConsoleColor.DarkCyan;
    // Replace value of "Red" from appsettings.json.
    configuration.LogLevelToColorMap[LogLevel.Error]
        = ConsoleColor.DarkRed;
});

using IHost host = builder.Build();

ILogger<Program> 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 используются для регистрации пользовательского поставщика:

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

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

Код создания экземпляра переопределяет значения цвета из файла appsettings.json для LogLevel.Warning и LogLevel.Error.

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

Пример вывода цветного консольного логгера

См. также