在 .NET 中實作自訂記錄提供者

有許多記錄提供者可用來滿足常見的記錄需求。 當其中一種提供者不符合您應用程式的需求時,您可能需要實作一個自訂 ILoggerProvider。 在本文中,您將了解如何實作自訂記錄提供者以在主控台中進行彩色記錄。

提示

Docs Github 存放庫提供自訂記錄提供者的原始程式碼範例。 如需詳細資訊,請參閱 GitHub:.NET Docs - 主控台自訂記錄 (英文)。

自訂記錄器設定範例

此範例會使用下列設定類型,為每個記錄層級和事件識別碼建立不同的彩色主控台項目:

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 實作類別名稱通常是記錄來源。 例如,建立記錄器的類型:

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 都有獨一無二的記錄器。 在此實作中,每個記錄層級都需要明確的設定項目來進行記錄。

建議您在 ILogger.Log 實作中呼叫 ILogger.IsEnabled,因為任何取用者都可以呼叫 Log,而且不能保證它先前有經過檢查。 在大部分的實作中,方法 IsEnabled 應該都非常快速。

TState state,
Exception? exception,

記錄器會透過 nameFunc<ColorConsoleLoggerConfiguration> 具現化並傳回目前的設定,這會處理透過 IOptionsMonitor<TOptions>.OnChange 回呼監控的設定值更新。

重要

ILogger.Log 實作會檢查是否已設定 config.EventId 值。 未設定 config.EventId 或該值符合 logEntry.EventId 時,記錄器會用顏色進行記錄。

自訂記錄器提供者

物件 ILoggerProvider 負責建立記錄器執行個體。 您不需要為每個類別建立記錄器執行個體,但對於 NLog 或 log4net 等記錄器來說,這麼做是合理的。 此策略可讓您為每個類別選擇不同的記錄輸出目標,如下列範例所示:

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 的設定,您可以在其提供者上定義一個別名:

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

類別 ColorConsoleLoggerProvider 會定義兩個以類別為範圍的屬性:

您可以使用任何有效的設定提供者來指定設定。 請考慮下列 appsettings.json 檔案:

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

這會將記錄層級設為下列值:

記錄層級 Information 已設為 DarkGreen,並且覆寫了 ColorConsoleLoggerConfiguration 物件中設定的預設值。

自訂記錄器的使用方式和註冊

依照慣例,相依性插入的註冊服務是應用程式啟動常式的一部分。 註冊會以 Program 類別發生,或可以委派給 Startup 類別。 在此範例中,您將直接從 Program.cs 進行註冊。

若要新增自訂記錄提供者和對應的記錄器,請從 HostingHostBuilderExtensions.ConfigureLogging(IHostBuilder, Action<ILoggingBuilder>) 新增一個具有 ILoggingBuilderILoggerProvider

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 會建立一或多個 ILogger 執行個體。 架構會使用 ILogger 執行個體來記錄資訊。

appsettings.json 檔案中的設定會覆寫下列值:

依照慣例,ILoggingBuilder 上的擴充方法可用來註冊自訂提供者:

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

執行這個簡單的應用程式可以將彩色輸出轉譯至主控台視窗,如下圖所示:

Color console logger sample output

另請參閱