Compartir vía


Implementación de un proveedor de registro personalizado en .NET

Hay muchos proveedores de registro disponibles para las necesidades de registro habituales. Es posible que tenga que implementar una ILoggerProvider personalizada cuando uno de los proveedores disponibles no se adapte a las necesidades de la aplicación. En este artículo, aprenderá a implementar un proveedor de registro personalizado que se puede usar para colorear los registros en la consola.

Sugerencia

El código fuente de ejemplo del proveedor de registro personalizado está disponible en el repositorio de GitHub de Docs. Para obtener más información, consulte GitHub: Documentos de .NET: Registro personalizado de consola.

Configuración de registrador personalizado de ejemplo

En el ejemplo se crean diferentes entradas de consola de color por nivel de registro e identificador de evento con el siguiente tipo de configuración:

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

El código anterior establece el nivel predeterminado en Information, el color en Green, y el EventId es implícitamente 0.

Creación del registrador personalizado

El nombre de la categoría de implementación ILogger es normalmente el origen del registro. Por ejemplo, el tipo donde se crea el registrador:

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

El código anterior:

  • Crea una instancia de registrador para cada nombre de categoría.
  • Comprueba _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel) en IsEnabled, por lo que cada logLevel tiene un registrador único. En esta implementación, cada nivel de registro requiere una entrada de configuración explícita para registrar.

Es recomendable llamar a ILogger.IsEnabled dentro de las implementaciones de ILogger.Log, dado que cualquier consumidor podría llamar a Log y no hay garantía de que se haya comprobado previamente. El método IsEnabled debe ser muy rápido en la mayoría de las implementaciones.

TState state,
Exception? exception,

El registrador es instanciado con el name y un Func<ColorConsoleLoggerConfiguration>, que devuelve la configuración actual-esto maneja las actualizaciones de los valores de configuración como monitoreado a través de la devolución de llamada IOptionsMonitor<TOptions>.OnChange.

Importante

La implementación de ILogger.Log verifica si el valor config.EventId está establecido. Cuando no se establece config.EventId o cuando coincide con el logEntry.EventIdexacto, el registrador registra en color.

Proveedor de registrador personalizado

El objeto ILoggerProvider es responsable de crear instancias de registrador. No es necesario crear una instancia de registrador por categoría, pero tiene sentido para algunos registradores, como NLog o log4net. Esta estrategia permite elegir diferentes destinos de salida de registro por categoría, como en el ejemplo siguiente:

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

En el código anterior, CreateLogger crea una única instancia del ColorConsoleLogger por nombre de categoría y la almacena en el ConcurrentDictionary<TKey,TValue>. Además, se requiere la interfaz IOptionsMonitor<TOptions> para actualizar los cambios en el objeto ColorConsoleLoggerConfiguration subyacente.

Para controlar la configuración del ColorConsoleLogger, defina un alias en su proveedor:

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

La clase ColorConsoleLoggerProvider define dos atributos con ámbito de clase:

La configuración se puede especificar con cualquier proveedor de configuración válido. Tenga en cuenta el siguiente archivo appsettings.json:

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

Esto configura los niveles de registro en los siguientes valores:

El nivel de registro de Information se establece en DarkGreen, que invalida el valor predeterminado establecido en el objeto ColorConsoleLoggerConfiguration.

Uso y registro del registrador personalizado

Por convención, el registro de servicios para la inserción de dependencias se produce como parte de la rutina de inicio de una aplicación. El registro se produce en la clase Program o puede delegarse en una clase Startup. En este ejemplo, se registrará directamente desde Program.cs.

Para agregar el proveedor de registro personalizado y el registrador correspondiente, agregue un ILoggerProvider con ILoggingBuilder desde 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();

El ILoggingBuilder crea una o varias instancias de ILogger. El marco usa las instancias de ILogger para registrar la información.

La configuración del archivo appsettings.json invalida los siguientes valores:

Por convención, los métodos de extensión de ILoggingBuilder se usan para registrar el proveedor personalizado:

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

Ejecutar esta sencilla aplicación generará una salida de color en la ventana de la consola, similar a la imagen siguiente.

Salida de ejemplo del registrador de la consola de color

Consulte también