Implémenter un fournisseur de journalisation personnalisé dans .NET

Il existe de nombreux fournisseurs de journalisation disponibles pour les besoins courants de journalisation. Vous devrez peut-être implémenter un fournisseur personnalisé ILoggerProvider lorsque l’un des fournisseurs disponibles ne répond pas aux besoins de votre application. Dans cet article, vous allez apprendre à implémenter un fournisseur de journalisation personnalisé qui peut être utilisé pour coloriser les journaux dans la console.

Conseil

L’exemple de code source du fournisseur de journalisation personnalisé est disponible dans le référentiel Github Docs. Pour plus d’informations, consultez GitHub : .NET Docs, Journalisation personnalisée console.

Exemple de configuration d’enregistreur d’événements personnalisé

L’exemple crée différentes entrées de console de couleur par niveau de journal et identifiant d’événement à l’aide du type de configuration suivant :

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

Le code précédent définit le niveau par défaut sur Information, la couleur sur Greenet l’EventId est implicitement 0.

Créer l’enregistreur d’événements personnalisé

Le nom de catégorie d’implémentation ILogger est généralement la source de journalisation. Par exemple, le type où l’enregistreur d’événements est créé :

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

Le code précédent :

  • Crée une instance d’enregistreur d’événements par nom de catégorie.
  • Vérifie _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel) dans IsEnabled, de sorte que chaque logLevel ait un enregistreur d’événements unique. Dans cette implémentation, chaque niveau de journal a besoin d’une entrée de configuration explicite à journaliser.

Il est recommandé d’appeler ILogger.IsEnabled dans les implémentations ILogger.Log, car Log peut être appelé par n’importe quel consommateur, et aucune garantie ne prouve qu’il a été vérifié précédemment. La méthode IsEnabled est très rapide dans la plupart des implémentations.

TState state,
Exception? exception,

L’enregistreur d’événements est instancié avec name et Func<ColorConsoleLoggerConfiguration>, qui retourne la configuration actuelle. Il gère les mises à jour des valeurs de configuration, comme prévu par le biais du rappel IOptionsMonitor<TOptions>.OnChange.

Important

L’implémentation ILogger.Log vérifie si la valeur config.EventId est définie. Quand config.EventId n’est pas défini ou lorsqu’il correspond à logEntry.EventId, l’enregistreur d'événements journalise en couleur.

Fournisseur d’enregistreurs d’événements personnalisés

L’objet ILoggerProvider est responsable de la création d’instances d’enregistreur d’événements. Il n’est pas nécessaire de créer une instance d’enregistreur d’événements par catégorie, mais il est logique pour certains enregistreurs d’événements, tels que NLog ou log4net. Cette stratégie vous permet de choisir différentes cibles de sortie de journalisation par catégorie, comme dans l’exemple suivant :

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

Dans le code précédent, CreateLogger crée une instance unique de ColorConsoleLogger par nom de catégorie et la stocke dans ConcurrentDictionary<TKey,TValue>. En outre, l’interface IOptionsMonitor<TOptions> est nécessaire pour mettre à jour les modifications apportées à l’objet sous-jacent ColorConsoleLoggerConfiguration.

Pour contrôler la configuration de ColorConsoleLogger, vous définissez un alias sur son fournisseur :

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

La classe ColorConsoleLoggerProvider définit deux attributs d’étendue de classe :

La configuration peut être spécifiée avec n’importe quel fournisseur de configuration valide. Considérez le fichier appsettings.json suivant :

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

Cela configure les niveaux de journal sur les valeurs suivantes :

Le niveau de journal Information est défini sur DarkGreen, qui remplace la valeur par défaut définie dans l’objet ColorConsoleLoggerConfiguration.

Utilisation et inscription de l’enregistreur d’événements personnalisé

Par convention, l’inscription de services pour l’injection de dépendances se produit dans le cadre de la routine de démarrage d’une application. L’inscription se produit dans la classe Program ou peut être déléguée à une classe Startup. Dans cet exemple, vous allez vous inscrire directement à partir du fichier Program.cs.

Pour ajouter le fournisseur de journalisation personnalisé et l’enregistreur d’événements correspondants, ajoutez ILoggerProvider avec ILoggingBuilder partir de 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 crée une ou plusieurs instances ILogger. Les instances ILogger sont utilisées par l’infrastructure pour enregistrer les informations.

La configuration du fichier appsettings.json remplace les valeurs suivantes :

Par convention, les méthodes d’extension sur ILoggingBuilder sont utilisées pour inscrire le fournisseur personnalisé :

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

L’exécution de cette application simple affiche la sortie de couleur dans la fenêtre de console similaire à l’image suivante :

Color console logger sample output

Voir aussi