Implementieren eines benutzerdefinierten Protokollierungsanbieters in .NET

Für Ihre allgemeinen Protokollierungsanforderungen stehen viele Protokollierungsanbieter zur Verfügung. Sie sollten einen benutzerdefinierten ILoggerProvider implementieren, wenn einer der folgenden Anbieter den Anforderungen für Ihre Anwendung nicht erfüllen kann. In diesem Artikel erfahren Sie, wie Sie einen benutzerdefinierten Protokollierungsanbieter implementieren, der zum Einfärben von Protokollen in der-Konsole verwendet werden kann.

Tipp

Der Beispielquellcode des benutzerdefinierten Protokollierungsanbieters ist im GitHub-Repository „Docs“ verfügbar. Weitere Informationen finden Sie unter GitHub: .NET-Dokumentation – Benutzerdefinierte Protokollierung in der Konsole.

Beispielkonfiguration für benutzerdefinierte Protokollierung

Im Beispiel werden unterschiedliche Farbkonsoleneinträge pro Protokolliergrad und Ereignis-ID mithilfe des folgenden Konfigurationstyps erstellt:

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

Der vorangehende Code legt den Standardprotokolliergrad auf Information und die Farbe auf Green fest, und EventId ist implizit 0.

Erstellen der benutzerdefinierten Protokollierung

Der Kategoriename der ILogger-Implementierung ist in der Regel die Protokollierungsquelle. Beispielsweise der Typ, in dem die Protokollierung erstellt wird:

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

Der vorangehende Code:

  • Erstellt eine Protokollierungsinstanz pro Kategoriename.
  • Überprüft _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel) in IsEnabled, sodass jedes logLevel-Element über eine eindeutige Protokollierung verfügt. Bei dieser Implementierung erfordert jede Protokollebene einen expliziten Konfigurationseintrag zum Protokollieren.

Es ist eine bewährte Methode, ILogger.IsEnabled innerhalb von ILogger.Log-Implementierungen aufzurufen, da Log von jedem Consumer aufgerufen werden kann, und es keine Garantien gibt, dass es zuvor überprüft wurde. Die IsEnabled-Methode sollte in den meisten Implementierungen sehr schnell sein.

TState state,
Exception? exception,

Die Protokollierung wird mit dem name und einer Func<ColorConsoleLoggerConfiguration> instanziiert, wodurch die aktuelle Konfiguration zurückgegeben wird. Damit werden Aktualisierungen der Konfigurationswerte gemäß der Überwachung durch den Rückruf von IOptionsMonitor<TOptions>.OnChange behandelt.

Wichtig

Die ILogger.Log-Implementierung überprüft, ob der config.EventId-Wert festgelegt ist. Wenn config.EventId nicht festgelegt ist oder genau mit logEntry.EventId übereinstimmt, erfolgt die Protokollierung in Farbe.

Benutzerdefinierter Protokollierungsanbieter

Das ILoggerProvider-Objekt ist für die Erstellung der Protokollierungsinstanzen verantwortlich. Es muss keine Protokollierungsinstanz pro Kategorie erstellt werden. Dies ist jedoch für einige Protokollierungen wie NLog oder log4net sinnvoll. Mit dieser Strategie können Sie verschiedene Protokollierungsausgabeziele wie im folgenden Beispiel pro Kategorie auswählen:

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

Im Code oben erstellt CreateLogger eine einzelne Instanz von ColorConsoleLogger pro Kategoriename und speichert sie in ConcurrentDictionary<TKey,TValue>. Außerdem ist die IOptionsMonitor<TOptions>-Schnittstelle erforderlich, um Änderungen am zugrunde liegenden ColorConsoleLoggerConfiguration-Objekt zu aktualisieren.

Um die Konfiguration von ColorConsoleLogger zu steuern, definieren Sie einen Alias für den zugehörigen Anbieter:

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

Die ColorConsoleLoggerProvider-Klasse definiert zwei klassenspezifische Attribute:

Die Konfiguration kann mit einem beliebigen gültigen Konfigurationsanbieter angegeben werden. Sehen Sie sich die nachfolgende Datei appsettings.json an:

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

Dadurch werden die Protokollebenen mit den folgenden Werten konfiguriert:

Die Protokollebene Information ist auf DarkGreen festgelegt, wodurch der im Objekt ColorConsoleLoggerConfiguration festgelegte Standardwert überschrieben wird.

Verwendung und Registrierung der benutzerdefinierten Protokollierung

Die Registrierung von Diensten für die Abhängigkeitsinjektion erfolgt, gemäß Konvention, als Teil Startroutine einer Anwendung. Die Registrierung erfolgt in der Program Klasse oder kann an eine Klasse delegiert Startup werden. In diesem Beispiel registrieren Sie sich direkt über program.cs.

Fügen Sie ILoggerProvider mit ILoggingBuilder aus HostingHostBuilderExtensions.ConfigureLogging(IHostBuilder, Action<ILoggingBuilder>) hinzu, um die benutzerdefinierten Protokollierungsanbieter und die entsprechenden Protokollierungen hinzuzufügen:

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 erstellt mindestens eine ILogger-Instanz. Die ILogger-Instanzen werden vom Framework zum Protokollieren der Informationen verwendet.

Die Konfiguration in der Datei appsettings.json überschreibt die folgenden Werte:

Gemäß Konvention werden Erweiterungsmethoden für ILoggingBuilder verwendet, um den benutzerdefinierten Anbieter zu registrieren:

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

Wenn Sie diese einfache Anwendung ausführen, wird die Farbausgabe im Konsolenfenster ähnlich wie in der folgenden Abbildung dargestellt:

Color console logger sample output

Siehe auch