英語で読む

次の方法で共有


.NET でカスタム ログ プロバイダーを実装する

一般的なログのニーズに使用できるログ プロバイダーは、数多くあります。 使用可能なプロバイダーの 1 つがアプリケーションのニーズに合わない場合は、カスタム ILoggerProvider の実装が必要になる場合があります。 この記事では、コンソールでログを色分けするために使用できるカスタム ログ プロバイダーを実装する方法について説明します。

ヒント

カスタム ログ プロバイダーのサンプル ソース コードは、Docs GitHub リポジトリで入手できます。 詳細については、「GitHub の .NET Docs - コンソールのカスタム ログ」を参照してください。

カスタム ロガー構成のサンプル

サンプルでは、次の構成の種類を使用して、ログ レベルとイベント ID ごとに異なる色のコンソール エントリを作成します。

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

上記のコードでは、既定のレベルが Informationに設定され、色が Greenに設定され、EventId が暗黙的に 0されます。

カスタム ロガーを作成する

ILogger 実装カテゴリ名は、通常、ログ ソースです。 たとえば、ロガーが作成される型は次のようになります。

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

上記のコード:

  • カテゴリ名ごとにロガー インスタンスを作成します。
  • IsEnabled_getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel) をチェックし、各 logLevel に一意のロガーが含まれるようにします。 この実装では、ログ レベルごとに、ログに明示的な構成エントリが必要です。

Log は任意のコンシューマーによって呼び出すことができるため、ILogger.Log 実装内で ILogger.IsEnabled を呼び出すのが良い方法であり、以前に確認されたことは保証されません。 IsEnabled メソッドは、ほとんどの実装で非常に高速である必要があります。

C#
TState state,
Exception? exception,

ロガーは、name と現在の構成を返す Func<ColorConsoleLoggerConfiguration>でインスタンス化されます。これにより、IOptionsMonitor<TOptions>.OnChange コールバックを介して監視された構成値の更新が処理されます。

重要

ILogger.Log 実装では、config.EventId 値が設定されているかどうかを確認します。 config.EventId が設定されていない場合、または正確な logEntry.EventIdと一致する場合、ロガーはカラーでログに記録します。

カスタム ロガー プロバイダー

ILoggerProvider オブジェクトは、ロガー インスタンスの作成を担当します。 カテゴリごとにロガー インスタンスを作成する必要はありませんが、NLog や log4net などの一部のロガーには適しています。 この方法では、次の例のように、カテゴリごとに異なるログ出力ターゲットを選択できます。

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

上記のコードでは、CreateLogger カテゴリ名ごとに ColorConsoleLogger の単一のインスタンスを作成し、ConcurrentDictionary<TKey,TValue>に格納します。 さらに、基になる ColorConsoleLoggerConfiguration オブジェクトへの変更を更新するには、IOptionsMonitor<TOptions> インターフェイスが必要です。

ColorConsoleLoggerの構成を制御するには、そのプロバイダーにエイリアスを定義します。

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

ColorConsoleLoggerProvider クラスは、次の 2 つのクラス スコープ属性を定義します。

構成は、任意の有効な 構成プロバイダーで指定できます。 次の appsettings.json ファイルについて考えてみます。

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

これにより、ログ レベルが次の値に構成されます。

Information ログ レベルは DarkGreenに設定され、ColorConsoleLoggerConfiguration オブジェクトで設定された既定値がオーバーライドされます。

カスタム ロガーの使用と登録

慣例により、依存関係の挿入のためのサービスの登録は、アプリケーションのスタートアップ ルーチンの一部として行われます。 登録は、Program クラスで行われるか、Startup クラスに委任できます。 この例では、Program.csから直接登録します。

カスタム ログ プロバイダーと対応するロガーを追加するには、HostingHostBuilderExtensions.ConfigureLogging(IHostBuilder, Action<ILoggingBuilder>)から ILoggingBuilder を含む ILoggerProvider を追加します。

C#
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 は、1 つ以上の ILogger インスタンスを作成します。 ILogger インスタンスは、情報をログに記録するためにフレームワークによって使用されます。

appsettings.json ファイルの構成は、次の値をオーバーライドします。

慣例により、ILoggingBuilder の拡張メソッドを使用してカスタム プロバイダーを登録します。

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

この単純なアプリケーションを実行すると、次の図のような色の出力がコンソール ウィンドウにレンダリングされます。

カラー コンソール ロガーのサンプル出力

関連項目