Partilhar via


Implementar um provedor de log personalizado no .NET

Há muitos provedores de registro disponíveis para necessidades comuns de registro. Talvez seja necessário implementar um personalizado ILoggerProvider quando um dos provedores disponíveis não atender às necessidades do seu aplicativo. Neste artigo, você aprenderá a implementar um provedor de log personalizado que pode ser usado para colorir logs no console.

Gorjeta

O código-fonte de exemplo do provedor de registro em log personalizado está disponível no repositório do Docs Github. Para obter mais informações, consulte GitHub: .NET Docs - Console Custom Logging.

Exemplo de configuração de logger personalizado

O exemplo cria entradas de console de cores diferentes por nível de log e ID de evento usando o seguinte tipo de configuração:

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

O código anterior define o nível padrão como Information, a cor como Green, e o EventId é implicitamente 0.

Criar o registador personalizado

O ILogger nome da categoria de implementação é normalmente a fonte de registro. Por exemplo, o tipo onde o registrador é criado:

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

O código anterior:

  • Cria uma instância de logger por nome de categoria.
  • Check-in _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel)IsEnabled, para que cada logLevel um tenha um registrador exclusivo. Nesta implementação, cada nível de log requer uma entrada de configuração explícita para o log.

É uma boa prática chamar ILogger.IsEnabled dentro ILogger.Log de implementações, uma vez Log que pode ser chamado por qualquer consumidor, e não há garantias de que foi previamente verificado. O IsEnabled método deve ser muito rápido na maioria das implementações.

TState state,
Exception? exception,

O registrador é instanciado com o name e um Func<ColorConsoleLoggerConfiguration>, que retorna a configuração atual — isso lida com atualizações para os valores de configuração conforme monitorado por meio do retorno de IOptionsMonitor<TOptions>.OnChange chamada.

Importante

A ILogger.Log implementação verifica se o config.EventId valor está definido. Quando config.EventId não está definido ou quando corresponde ao exato logEntry.EventId, o registador regista a cores.

Provedor de registro personalizado

O ILoggerProvider objeto é responsável pela criação de instâncias de logger. Não é necessário criar uma instância de logger por categoria, mas faz sentido para alguns loggers, como NLog ou log4net. Essa estratégia permite que você escolha diferentes destinos de saída de log por categoria, como no exemplo a seguir:

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

No código anterior, CreateLogger cria uma única instância do nome por categoria e armazena-a ColorConsoleLoggerConcurrentDictionary<TKey,TValue>no . Além disso, a IOptionsMonitor<TOptions> interface é necessária para atualizar as alterações no objeto subjacente ColorConsoleLoggerConfiguration .

Para controlar a configuração do ColorConsoleLogger, defina um alias em seu provedor:

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

A ColorConsoleLoggerProvider classe define dois atributos de escopo de classe:

A configuração pode ser especificada com qualquer provedor de configuração válido. Considere o seguinte arquivo appsettings.json :

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

Isso configura os níveis de log para os seguintes valores:

O Information nível de log é definido como DarkGreen, que substitui o ColorConsoleLoggerConfiguration valor padrão definido no objeto.

Utilização e registo do registador personalizado

Por convenção, o registro de serviços para injeção de dependência acontece como parte da rotina de inicialização de um aplicativo. O registro ocorre na Program classe, ou pode ser delegado a uma Startup classe. Neste exemplo, você se registrará diretamente do Program.cs.

Para adicionar o provedor de log personalizado e o registrador correspondente, adicione um ILoggerProvider com ILoggingBuilder do 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();

O ILoggingBuilder cria uma ou mais ILogger instâncias. As ILogger instâncias são usadas pela estrutura para registrar as informações.

A configuração do arquivo appsettings.json substitui os seguintes valores:

Por convenção, os métodos de extensão em ILoggingBuilder são usados para registrar o provedor 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;
    }
}

A execução deste aplicativo simples renderizará a saída de cores para a janela do console semelhante à seguinte imagem:

Color console logger sample output

Consulte também