Implementowanie niestandardowego dostawcy rejestrowania na platformie .NET

Istnieje wiele dostawców rejestrowania dostępnych dla typowych potrzeb rejestrowania. Może być konieczne zaimplementowanie niestandardowego ILoggerProvider , gdy jeden z dostępnych dostawców nie odpowiada potrzebom aplikacji. W tym artykule dowiesz się, jak zaimplementować niestandardowego dostawcę rejestrowania, który może służyć do kolorowania dzienników w konsoli programu .

Napiwek

Przykładowy kod źródłowy niestandardowego dostawcy rejestrowania jest dostępny w repozytorium GitHub witryny Docs. Aby uzyskać więcej informacji, zobacz GitHub: .NET Docs — rejestrowanie niestandardowe konsoli.

Przykładowa niestandardowa konfiguracja rejestratora

Przykład tworzy różne wpisy konsoli kolorów na poziom dziennika i identyfikator zdarzenia przy użyciu następującego typu konfiguracji:

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

Powyższy kod ustawia domyślny poziom na Information, kolor na Green, a element EventId jest niejawnie 0.

Tworzenie niestandardowego rejestratora

ILogger Nazwa kategorii implementacji jest zazwyczaj źródłem rejestrowania. Na przykład typ, w którym jest tworzony rejestrator:

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

Powyższy kod ma następujące działanie:

  • Tworzy wystąpienie rejestratora na nazwę kategorii.
  • Zaewidencjonuj _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel) element IsEnabled, więc każdy z nich logLevel ma unikatowy rejestrator. W tej implementacji każdy poziom dziennika wymaga jawnego wpisu konfiguracji do rejestrowania.

Dobrym rozwiązaniem jest wywołanie ILogger.IsEnabled implementacji, ILogger.Log ponieważ Log może być wywoływane przez dowolnego konsumenta i nie ma żadnych gwarancji, że został on wcześniej sprawdzony. Metoda IsEnabled powinna być bardzo szybka w większości implementacji.

TState state,
Exception? exception,

Rejestrator jest tworzone wystąpienie elementu name i Func<ColorConsoleLoggerConfiguration>, który zwraca bieżącą konfigurację — obsługuje aktualizacje wartości konfiguracji monitorowanych za pośrednictwem wywołania zwrotnego IOptionsMonitor<TOptions>.OnChange .

Ważne

Implementacja ILogger.Log sprawdza, czy wartość jest ustawiona config.EventId . Jeśli config.EventId nie jest ustawiona lub gdy jest zgodna z dokładną logEntry.EventIdwartością , rejestrator rejestruje kolor.

Niestandardowy dostawca rejestratora

Obiekt ILoggerProvider jest odpowiedzialny za tworzenie wystąpień rejestratora. Nie jest konieczne utworzenie wystąpienia rejestratora dla każdej kategorii, ale ma sens w przypadku niektórych rejestratorów, takich jak NLog lub log4net. Ta strategia pozwala wybrać różne cele wyjściowe rejestrowania dla każdej kategorii, jak w poniższym przykładzie:

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

W poprzednim kodzie CreateLogger tworzy pojedyncze wystąpienie ColorConsoleLogger nazwy kategorii i przechowuje je w pliku ConcurrentDictionary<TKey,TValue>. IOptionsMonitor<TOptions> Ponadto interfejs jest wymagany do aktualizowania zmian w obiekcie bazowymColorConsoleLoggerConfiguration.

Aby kontrolować konfigurację programu ColorConsoleLogger, należy zdefiniować alias u jego dostawcy:

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

Klasa ColorConsoleLoggerProvider definiuje dwa atrybuty o zakresie klasy:

Konfigurację można określić za pomocą dowolnego prawidłowego dostawcy konfiguracji. Rozważ następujący plik appsettings.json :

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

Spowoduje to skonfigurowanie poziomów dziennika na następujące wartości:

Poziom Information dziennika jest ustawiony na DarkGreenwartość , która zastępuje wartość domyślną ustawioną ColorConsoleLoggerConfiguration w obiekcie.

Użycie i rejestracja niestandardowego rejestratora

Zgodnie z konwencją rejestrowanie usług do wstrzykiwania zależności odbywa się w ramach procedury uruchamiania aplikacji. Rejestracja odbywa się w Program klasie lub może być delegowana do Startup klasy. W tym przykładzie zarejestrujesz się bezpośrednio z Program.cs.

Aby dodać niestandardowego dostawcę rejestrowania i odpowiadający mu rejestrator, dodaj element ILoggerProvider z ILoggingBuilder elementu 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();

Obiekt ILoggingBuilder tworzy co najmniej jedno ILogger wystąpienie. Wystąpienia ILogger są używane przez platformę do rejestrowania informacji.

Konfiguracja z pliku appsettings.json zastępuje następujące wartości:

Zgodnie z konwencją metody rozszerzeń są ILoggingBuilder używane do rejestrowania dostawcy niestandardowego:

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

Uruchomienie tej prostej aplikacji spowoduje renderowanie danych wyjściowych koloru w oknie konsoli, podobnie jak na poniższej ilustracji:

Color console logger sample output

Zobacz też