共用方式為


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

有許多 記錄提供者 可用於常見的記錄需求。 但當現有供應商不符合你的應用需求時,可能需要實施自訂 ILoggerProvider 方案。 在本文中,你將學習如何實作一個自訂的日誌服務提供者,該服務可以用來在主控台中為日誌上色。

小提示

自訂日誌提供者範例原始碼可在 文件的 GitHub 倉庫中找到。

範例自定義記錄器組態

範例記錄器會依照日誌層級和事件 ID 建立不同的色彩主控台條目,使用以下設定類型:

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)所請求的日誌層級是否啟用(即在組態的日誌層級對應表中)。

最好是在 ILogger.IsEnabled 實作內呼叫 ILogger.Log,因為任何使用者都可以呼叫 Log,且不保證先前已進行檢查。 IsEnabled 方法在大部分的實作中應該非常快速。

if (!IsEnabled(logLevel))
{
    return;
}

記錄器以 name 和一個 Func<ColorConsoleLoggerConfiguration> 實例化,後者回傳當前配置。

這很重要

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(String) 會為每個類別名稱建立 ColorConsoleLogger 的單一實例,並將它儲存在 ConcurrentDictionary<TKey,TValue>中。

ColorConsoleLoggerProvider 職業擁有兩項屬性:

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

可以藉由任何有效的 組態提供者來指定組態,。 以下列 appsettings.json 檔案為例:

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

appsettings.json 檔案指定對數層級的顏色InformationDarkGreen,這會覆蓋物件中ColorConsoleLoggerConfiguration預設的值。

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

依慣例,服務會在應用程式啟動例程中註冊以注入相依性。 在此範例中,日誌服務是直接從 Program.cs 檔案註冊的。

若要新增自訂日誌提供者及對應的日誌工具,請在IHostApplicationBuilder.Logging屬性的ILoggingBuilder上,呼叫自訂擴充方法AddColorConsoleLogger以加入ILoggerProvider

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 value of "Cyan" from appsettings.json.
    configuration.LogLevelToColorMap[LogLevel.Warning]
        = ConsoleColor.DarkCyan;
    // Replace value of "Red" from appsettings.json.
    configuration.LogLevelToColorMap[LogLevel.Error]
        = ConsoleColor.DarkRed;
});

using IHost host = builder.Build();

ILogger<Program> 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 上的擴充方法可用來註冊自定義提供者:

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

ILoggingBuilder 會建立一或多個 ILogger 實例。 架構會使用 ILogger 實例來記錄資訊。

實例化程式碼會覆蓋 appsettings.json 檔案中 和 LogLevel.WarningLogLevel.Error的顏色值。

當你執行這個簡單的應用程式時,它會將顏色輸出輸出到主控台視窗,類似以下圖片:

色彩控制台記錄器範例輸出

另請參閱