如何定製.NET6.0的日誌記錄

張飛洪[廈門]發表於2022-05-19

在本章中,也就是整個系列的第一部分將介紹如何定製日誌記錄(系列內容查閱《玩轉ASP.NET 6.0框架-序言》)。預設日誌記錄僅寫入控制檯或除錯視窗,這在大多數情況下都很好,但有時需要寫入到檔案或資料庫,或者,您可能希望擴充套件日誌記錄的其他資訊。在這些情況下,您需要知道如何更改預設日誌記錄。

在本章,我們將介紹以下主題:

  • 配置日誌記錄
  • 建立自定義日誌記錄
  • 使用第三方日誌框架

以上主題涉及ASP.NET Core框架的Host層。

技術要求

為了演示,我們建立一個ASP.NET Core MVC應用程式。請開啟控制檯、shellBash終端,然後切換到工作目錄,然後使用以下命令建立新的應用程式:

dotnet new mvc -n LoggingSample -o LoggingSample

Visual Studio中雙擊開啟該專案,或者在控制檯中鍵入以下命令用Visual Studio Code開啟該專案:

cd LoggingSample code .

配置日誌記錄

ASP.NET Core的早期版本中(即2.0版之前的版本),日誌記錄是在Startup.cs配置的。之後Startup.cs檔案慢慢簡化,許多配置被移動到Program.csWebHostBuilder,日誌記錄也是在這個時候被移動到WebHostBuilder

ASP.NET Core 3.1及更高版本的程式,Program.cs檔案變得更加通用,IHostBuilder將最先建立,它是引導應用啟動的關鍵(我們將在後面詳解IHostBuilder),通過IHostBuilder,我們可以建立IWebHostBuilder用以配置ASP.NET Core

public class Program {     
    public static void Main(string[] args)
    {         
        CreateHostBuilder(args).Build().Run();     
    }     
    
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => 
        { 
            webBuilder.UseStartup<Startup>();
        }); 
}

ASP.NET Core 6.0中,Microsoft引入了簡化配置的迷你API(minimal API)方法。這種方法不使用Startup檔案,而是將所有配置新增到Program.cs檔案,如下程式碼段:

var builder = WebApplication.CreateBuilder(args); 

//新增服務到容器.
builder.Services.AddControllersWithViews(); 
var app = builder.Build(); 
…

ASP.NET Core,您可以覆蓋和自定義幾乎所有內容,包括日誌記錄。IWebHostBuilder有很多擴充套件方法,允許我們覆蓋不同功能的預設行為。要覆蓋日誌記錄的預設設定,我們需要使用ConfigureLogging方法。

以下程式碼片段顯示的日誌記錄與上面的ConfigureWebHostDefaults()方法中配置的日誌記錄幾乎完全相同:

Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{         
    webBuilder.ConfigureLogging((hostingContext, logging) => {
        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));                 
        logging.AddConsole();                 
        logging.AddDebug();             
    }).UseStartup<Startup>();

使用minimal API方法,我們不再需要ConfigureLogging方法,我們可以直接使用WebApplicationBuilderLogging屬性:

builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging")); 
builder.Logging.AddConsole(); 
builder.Logging.AddDebug();

現在我們已經瞭解瞭如何配置日誌記錄,接下來我們看看如何自定義日誌記錄。

建立自定義日誌記錄

為了演示,我們這裡建立一個小而簡單的日誌記錄器,它能夠在控制檯中使用特定的日誌級別對日誌條目進行著色。此日誌記錄稱為ColoredConsoleLogger,它會使用LoggerProvider建立。要指定顏色和日誌級別,我們還需要新增一個配置類Configuration

在接下來的程式碼片段中,將分別建立這三個關鍵類(ConfigurationLoggerProviderLogger):

1.ColoredConsoleLoggerConfiguration

我們建立一個名為CustomLogger.cs的檔案中,它與Program.cs位於同一資料夾中,我們在CustomLogger.cs中建立ColoredConsoleLoggerConfiguration,該配置類包含三個可設定的屬性:LogLevelEventIdColor

public class ColoredConsoleLoggerConfiguration 
{     
    public LogLevel LogLevel { get; set; } = LogLevel.Warning;     
    public int EventId { get; set; } = 0;    
    public ConsoleColor Color { get; set; } = ConsoleColor.Yellow; 
}

2.ColoredConsoleLoggerProvider

接下來,我們需要一個提供程式來檢索配置並建立實際的日誌記錄例項

public class ColoredConsoleLoggerProvider : ILoggerProvider 
{     
    private readonly ColoredConsoleLoggerConfiguration _config;     
    private readonly ConcurrentDictionary<string, ColoredConsoleLogger> _loggers = new ConcurrentDictionary<string,ColoredConsoleLogger>();     
    
    public ColoredConsoleLoggerProvider (ColoredConsoleLoggerConfiguration config) 
    { 
        _config = config; 
    }     
    
    public ILogger CreateLogger(string categoryName) 
    { 
        return _loggers.GetOrAdd(categoryName,name => new ColoredConsoleLogger(name, _config));     
    }
    
    public void Dispose() 
    {
        _loggers.Clear(); 
    } 
}

不要忘記引入System.Collections.Concurrent

3.ColoredConsoleLogger

第三類是我們真正使用的日誌記錄器:

public class ColoredConsoleLogger : ILogger
{
    private static readonly object _lock = new Object();
    private readonly string _name;
    private readonly ColoredConsoleLoggerConfiguration _config;

    public ColoredConsoleLogger(
        string name,
        ColoredConsoleLoggerConfiguration config)
    {
        _name = name;
        _config = config;
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return logLevel == _config.LogLevel;
    }

    public void Log<TState>(
        LogLevel logLevel,
        EventId eventId,
        TState state,
        Exception exception,
        Func<TState, Exception, string> formatter)
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        lock (_lock)
        {
            if (_config.EventId == 0 || _config.EventId == eventId.Id)
            {
                var color = Console.ForegroundColor;
                Console.ForegroundColor = _config.Color;
                Console.Write($"{logLevel} - ");
                Console.Write($"{eventId.Id} - {_name} - ");
                Console.Write($"{formatter(state, exception)}\n");
                Console.ForegroundColor = color;
            }
        }
    }
}

我們現在需要lock(鎖定) 控制檯的輸出——這是因為控制檯本身並不是真正的執行緒安全的,可能出現錯誤的著色。

完成後,我們可以將新的記錄插入到Program.cs的配置中。

using LoggingSample;

builder.Logging.ClearProviders();
var config = new ColoredConsoleLoggerConfiguration
{
    LogLevel = LogLevel.Information,
    Color = ConsoleColor.Red
};
builder.Logging.AddProvider(new ColoredConsoleLoggerProvider(config));

首先需要向引入LoggerSample名稱空間。

using LoggingSample;

如果不想使用現有的日誌框架,可以清除之前新增的所有日誌框架提供程式

builder.Logging.ClearProviders();

然後,我們呼叫AddProvider來新增ColoredConsoleLoggerProvider例項。

這裡配置了不同的日誌級別,您可以使用這種方法傳送有關錯誤的電子郵件,或者將除錯訊息記錄到別的日誌接收器等等。

下圖顯示了日誌框架的彩色輸出:

在許多時候,編寫自定義日誌框架是沒有意義的,因為已經有許多優秀的第三方日誌記錄框架可用,例如ELMAHlog4netNLog

下面,我們將介紹如何在ASP.NET Core中使用NLog

使用第三方日誌框架NLog

NLog是最早的一款可用於ASP.NET Core的日誌框架,NLog提供了一個日誌記錄提供程式外掛,可以方便地插入ASP.NET Core。(你可以通過NuGet找到NLog

1.配置Nlog

我們需要新增一個NLog.Config配置檔案,用於定義兩個不同的日誌記錄:

  1. 所有標準訊息記錄在一個日誌檔案中;
  2. 而自定義訊息記錄在另一個檔案中
<targets>
     <!-- 標準訊息 -->
     <target xsi:type="File" name="allfile" fileName="C:\git\dotnetconf\001-logging\nlog-all-${shortdate}.log"
                 layout="${longdate}|${event-properties:item=EventId.Id}|${logger}|${uppercase:${level}}|${message} ${exception}" />

     
     <!-- 自定義訊息 -->
     <target xsi:type="File" name="ownFile-web" fileName="C:\git\dotnetconf\001-logging\nlog-own-${shortdate}.log"
             layout="${longdate}|${event-properties:item=EventId.Id}|${logger}|${uppercase:${level}}|  ${message} ${exception}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
      
     <target xsi:type="Null" name="blackhole" />
  </targets>

  <!-- rules to map from logger name to target -->
  <rules>
    <!--All logs, including from Microsoft-->
    <logger name="*" minlevel="Trace" writeTo="allfile" />

    <!--Skip Microsoft logs and so log only own logs-->
    <logger name="Microsoft.*" minlevel="Trace" writeTo="blackhole" final="true" />
    <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
  </rules>

2.引入NuGet包

然後我們需要NuGet新增NLogASP.NET Core包:

dotnet add package NLog.Web.AspNetCore

3.將NLogIWebHostBuilder結合使用

清除ConfigureLogging方法中的其他提供程式,使用UseNLog()方法將NLogIWebHostBuilder結合使用:

Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => {         
    webBuilder.ConfigureLogging((hostingContext,logging) => {      
        //清除其他提供程式       
        logging.ClearProviders();                 
        logging.SetMinimumLevel(LogLevel.Trace);             
    }).UseNLog().UseStartup<Startup>();     
});

使用minimal API看起來簡單得多:

using NLog.Web; 

var builder = WebApplication.CreateBuilder(args); 

//清除其他提供程式  
builder.Logging.ClearProviders(); 
builder.Logging.SetMinimumLevel(LogLevel.Trace); 
builder.WebHost.UseNLog();

在這裡,您可以根據需要新增任意多個日誌記錄提供程式。

回顧 & 思考

現在,讓我們回顧一下本文所涵蓋的內容:

  • 配置日誌記錄
  • 建立自定義日誌記錄
  • 使用第三方日誌框架

思考:我們應該如何自定義應用的配置?歡迎關注下篇內容《如何自定義.NET 6.0的應用配置》。

相關文章