.NET 8使用日誌功能以及自定義日誌提供程式

青春似雨后霓虹發表於2024-04-06

.NET 8使用日誌功能以及自定義日誌提供程式

日誌級別

下表列出了 LogLevel 值、方便的 Log{LogLevel} 擴充套件方法以及建議的用法:

展開表

LogLevel “值” 方法 描述
Trace 0 LogTrace 包含最詳細的訊息。 這些訊息可能包含敏感的應用資料。 這些訊息預設情況下處於禁用狀態,並且不應在生產中啟用。
除錯 1 LogDebug 用於除錯和開發。 由於量大,請在生產中小心使用。
資訊 2 LogInformation 跟蹤應用的常規流。 可能具有長期值。
警告 3 LogWarning 對於異常事件或意外事件。 通常包括不會導致應用失敗的錯誤或情況。
錯誤 4 LogError 表示無法處理的錯誤和異常。 這些訊息表示當前操作或請求失敗,而不是整個應用失敗。
嚴重 5 LogCritical 需要立即關注的失敗。 例如資料丟失、磁碟空間不足。
6 指定不應寫入任何訊息。

一、使用log4net

1、安裝需要的Nuget包

在專案中使用程式包管理器控制檯安裝log4net包

Install-Package log4net

如果在AspNetCore專案還需要安裝 Microsoft.Extensions.Logging.Log4Net.AspNetCore

Install-Package Microsoft.Extensions.Logging.Log4Net.AspNetCore

也可以使在Nuget管理程式中,搜尋 "Microsoft.Extensions.Logging.Log4Net.AspNetCore",然後點選安裝。

2、程式碼中使用log4net日誌元件

2.1、新建一個log4net.config配置檔案

配置檔案參考如下

<?xml version="1.0" encoding="utf-8" ?>

<!-- log4net.config -->
<log4net>

    <!--通用日誌類-->
    <!--日誌類的名字-->
    <logger name="Common">
        <!--定義記錄的日誌級別-->
        <level value="ALL" />
        <!--記錄到哪個介質中去-->
        <appender-ref ref="RollingLogFileAppender" />
    </logger>

    <!--其中layout節點的配置說明:
        %m(message):輸出的日誌訊息;
        %n(newline):換行;
		%d(datetime):輸出當前語句執行的時刻;
		%r(runtime):輸出程式從執行到執行到當前語句時消耗的毫秒數;
		%t(threadid):當前語句所在的執行緒ID ;
		%p(priority): 日誌的當前日誌級別;
		%c(class):當前日誌物件的名稱;
		%L:輸出語句所在的行號;
		%F:輸出語句所在的檔名;
		%-10:表示最小長度為10,如果不夠,則用空格填充;-->
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
        <!--日誌路徑-->
        <param name= "File" value= "Logs/"/>
        <!--多執行緒時採用最小鎖定-->
        <lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
        <!--是否是向檔案中追加日誌-->
        <param name= "AppendToFile" value= "true"/>
        <!--log保留天數-->
        <!--<param name= "MaxSizeRollBackups" value= "10"/>-->
        <!--日誌檔名是否是固定不變的-->
        <param name= "StaticLogFileName" value= "false"/>
        <!--日誌檔名格式為:2022-05-22.log-->
        <param name= "DatePattern" value= "yyyy-MM-dd'.log'"/>
        <!--日誌根據日期滾動-->
        <param name= "RollingStyle" value= "Date"/>
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%n%d [%t] %-5p %c [%L] - %m %n" />
        </layout>
    </appender>

    <root>
        <!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) -->
        <level value="all" />
        <!--<appender-ref ref="ColoredConsoleAppender"/>-->
        <appender-ref ref="RollingLogFileAppender"/>
    </root>
</log4net>


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
	</configSections>
	<log4net debug="false">

		<appender name="info" type="log4net.Appender.RollingFileAppender,log4net">
			<param name="File" value="log4net/info/" />
			<param name="AppendToFile" value="true" />
			<param name="MaxSizeRollBackups" value="-1"/>
			<param name="MaximumFileSize" value="5MB"/>
			<param name="RollingStyle" value="Composite" />
			<param name="DatePattern" value="yyyyMMdd\\HH&quot;.log&quot;" />
			<param name="StaticLogFileName" value="false" />
			<layout type="log4net.Layout.PatternLayout,log4net">
				<param name="ConversionPattern" value="%n
{
    &quot;system&quot;: &quot;Meowv.Blog&quot;,
    &quot;datetime&quot;: &quot;%d&quot;,
    &quot;description&quot;: &quot;%m&quot;,
  &quot;level&quot;: &quot;%p&quot;,
    &quot;info&quot;: &quot;%exception&quot;
}" />
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter">
				<levelMin value="INFO" />
				<levelMax value="INFO" />
			</filter>
		</appender>

		<appender name="error" type="log4net.Appender.RollingFileAppender,log4net">
			<param name="File" value="log4net/error/" />
			<param name="AppendToFile" value="true" />
			<param name="MaxSizeRollBackups" value="-1"/>
			<param name="MaximumFileSize" value="5MB"/>
			<param name="RollingStyle" value="Composite" />
			<param name="DatePattern" value="yyyyMMdd\\HH&quot;.log&quot;" />
			<param name="StaticLogFileName" value="false" />
			<layout type="log4net.Layout.PatternLayout,log4net">
				<param name="ConversionPattern" value="%n
{
    &quot;system&quot;: &quot;Meowv.Blog&quot;,
    &quot;datetime&quot;: &quot;%d&quot;,
    &quot;description&quot;: &quot;%m&quot;,
  &quot;level&quot;: &quot;%p&quot;,
    &quot;info&quot;: &quot;%exception&quot;
}" />
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter">
				<levelMin value="ERROR" />
				<levelMax value="ERROR" />
			</filter>
		</appender>

		<root>
			<level value="ALL"></level>
			<appender-ref ref="info"/>
			<appender-ref ref="error"/>
		</root>

	</log4net>

</configuration>

<?xml version="1.0" encoding="utf-8"?>
<!--log4net日誌配置資訊-->
<log4net>
	<!-- 控制檯日誌配置 -->
	<appender name="Console" type="log4net.Appender.ConsoleAppender">
		<!-- 日誌輸出格式 -->
		<layout type="log4net.Layout.PatternLayout">
			<conversionPattern value="%5level [%thread] (%file:%line) - %message%newline" />
		</layout>
	</appender>
	<!-- 檔案儲存日誌配置 -->
	<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
		<!--目錄路徑,可以是相對路徑或絕對路徑-->
		<file value="log\" />
		<!--追加日誌內容-->
		<appendToFile value="true" />
		<!--防止多執行緒時不能寫Log,官方說執行緒非安全-->
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<!--可以為:Once|Size|Date|Composite-->
		<!--Composite為Size和Date的組合-->
		<rollingStyle value="Composite" />
		<!--日期的格式,每天換一個檔案記錄,如不設定則永遠只記錄一天的日誌,需設定-->
		<datePattern value="yyyy-MM-dd&quot;.txt&quot;" />
		<!--檔名,按日期生成資料夾-->
		<!--<param name="DatePattern" value="/yyyy-MM-dd/"Error.log=""""/>-->
		<!-- 儲存檔案數量 -->
		<!--日誌最大個數,都是最新的-->
		<!--rollingStyle節點為Size時,只能有value個日誌-->
		<!--rollingStyle節點為Composite時,每天有value個日誌-->
		<!--最多保留的檔案數,設為"-1"則不限-->
		<maxSizeRollBackups value="20" />
		<!-- 檔案的編碼方式 -->
		<param name="Encoding" value="UTF-8"/>
		<!-- 每個檔案的大小 -->
		<!--可用的單位:KB|MB|GB-->
		<maximumFileSize value="3MB" />
		<!--置為true,當前最新日誌檔名永遠為file節中的名字-->
		<staticLogFileName value="false" />
		<!-- 日誌輸出格式 -->
		<layout type="log4net.Layout.PatternLayout">
			<!--<conversionPattern value="%level %thread %logger - %message%newline" />-->
			<param name="ConversionPattern" value="%newline %n記錄時間:%date %n執行緒ID:[%thread] %n日誌級別:%-5level %n出錯類:%logger %n請求URI:%message %n異常資訊:%exception%newline %n" />
		</layout>
	</appender>
	<!--SQL資料庫-->
	<appender name="AdoNetAppender" type="MicroKnights.Logging.AdoNetAppender, MicroKnights.Log4NetAdoNetAppender">
		<bufferSize value="1" />
		<!-- SQL資料來源-->
		<connectionType value="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient, Version=1.0.0.0,Culture=neutral,PublicKeyToken=23ec7fc2d6eaa4a5"/>
		<!-- SQL連線字串-->
		<!--<connectionString value="Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=YMHealth;Data Source=." />-->
		<connectionString value="Integrated Security=False;Data Source=47.110.55.108;Initial Catalog=BreastHealthbat;User Id=sa;pwd=Sinoadmin@136;" />
		<!--<connectionString value="Integrated Security=False;Data Source=.;Initial Catalog=BreastHealth;User Id=sa;pwd=sasino;" />-->
		<commandText value="INSERT INTO TLog ([ThreadId],[LogLevel],[Logger],[LogMessage],[LogException],[LogDate]) VALUES  (@thread,@logLevel,@logger,@message,@exception,@logDate)" />
		<parameter>
			<parameterName value="@thread" />
			<dbType value="String" />
			<size value="5" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%t" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@logLevel" />
			<dbType value="String" />
			<size value="5" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%p" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@logger" />
			<dbType value="String" />
			<size value="3000" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%logger" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@message" />
			<dbType value="String" />
			<size value="3000" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%message" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@exception" />
			<dbType value="String" />
			<size value="3000" />
			<layout type="log4net.Layout.ExceptionLayout" />
		</parameter>
		<parameter>
			<parameterName value="@logDate" />
			<dbType value="DateTime" />
			<layout type="log4net.Layout.RawTimeStampLayout" />
		</parameter>
	</appender>
	<!--根配置-->
	<root>
		<!--日誌級別:可選值: ERROR > WARN > INFO > DEBUG -->
		<level value="ERROR" />
		<!--<appender-ref ref="Console" />
		<appender-ref ref="RollingFile" />-->
		<appender-ref ref="AdoNetAppender" />
		<appender-ref ref="RollingFileAppender" />
	</root>
</log4net>

2.2、設定檔案為始終複製

2.3、在控制檯中使用

using log4net.Config;
using log4net;
using System.Reflection;

namespace Test.ConsoleApp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var log4netRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
            XmlConfigurator.Configure(log4netRepository, new FileInfo("log4net.config"));
            var log = LogManager.GetLogger(log4netRepository.Name, "NETCorelog4net");

            log.Info("NETCorelog4net log");
            log.Info("test log");
            log.Error("error");
            log.Info("linezero");
            Console.ReadKey();
        }
    }
}

2.4、在AspNetCore專案中使用

方式一

//預設路徑是根目錄的log4net.config,也可以更改log4net.config路徑,和名稱
builder.Logging.AddLog4Net("Configs/log4net.config");

方式二

builder.Services.AddLogging(logging =>
{
    //預設的配置檔案路徑是在根目錄,且檔名為log4net.config
    //logging.AddLog4Net();
    logging.AddLog4Net("Configs/log4net.config");

    //如果檔案路徑或名稱有變化,需要重新設定其路徑或名稱
    //比如在專案根目錄下建立一個名為cfg的資料夾,將log4net.config檔案移入其中,並改名為log.config
    //則需要使用下面的程式碼來進行配置
    //logging.AddLog4Net(new Log4NetProviderOptions()
    //{
    //    Log4NetConfigFileName = "cfg/log.config",
    //    Watch = true
    //});
});

方式三(有問題,在.NET6之前的Startup類Configure方法注入有用)

//也可以使用ILoggerFactory注入log4net
//loggerFactory.AddLog4Net();
using ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddLog4Net("Configs/log4net.config");
});

參考文件:

https://www.cnblogs.com/wei325/p/16000271.html

https://github.com/huorswords/Microsoft.Extensions.Logging.Log4Net.AspNetCore

https://www.cnblogs.com/shangec/p/14666007.html

https://www.cnblogs.com/vvull/p/17967654

https://docs.meowv.com/stack/dotnetcore/log4net-in-dotnet.html

二、使用Nlog

1、控制檯專案

1.1、安裝如下Nuget包

Install-Package Microsoft.Extensions.Configuration.Json
Install-Package NLog
Install-Package NLog.Extensions.Logging

1.2、在控制檯專案建立appsetting.json檔案,檔案內容如下

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

1.3、建立nlog.config檔案,並設定屬性為始終複製,下面是config檔案參考

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Warn"
      internalLogFile="internal-nlog.txt">

  <!-- define various log targets -->
  <targets>
    <!-- write logs to file -->
    <target xsi:type="File" name="allfile" fileName="nlog-all-${shortdate}.log"
                 layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />


    <target xsi:type="File" name="ownFile-web" fileName="nlog-own-${shortdate}.log"
             layout="${longdate}|${logger}|${uppercase:${level}}|  ${message} ${exception}" />

    <target xsi:type="Null" name="blackhole" />
  </targets>

  <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>
</nlog>

1.4、新增一個類Runner.cs

using Microsoft.Extensions.Logging;

namespace ConsoleDemo
{
    public class Runner
    {
        private readonly ILogger<Runner> _logger;

        public Runner(ILogger<Runner> logger)
        {
            _logger = logger;
        }

        public void DoAction(string name)
        {
            _logger.LogDebug(20, "Doing hard work! {Action}", name);
        }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Info"
      internalLogFile="nlog\internal-nlog.txt">

	<!-- the targets to write to -->
	<targets>
		<!-- write logs to file -->
		<target xsi:type="File" name="logfile" fileName="nlog\console${shortdate}.log"
            layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />
		<target xsi:type="Console" name="logconsole"
				layout="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}" />

		<target xsi:type="Null" name="blackhole" />
	</targets>
	<!-- rules to map from logger name to target -->
	<rules>
		<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
	</rules>
</nlog>

1.5、透過注入的方式呼叫。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Extensions.Logging;
using System;
namespace Test.ConsoleApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            #region nlog
            var logger = LogManager.GetCurrentClassLogger();
            try
            {
                var config = new ConfigurationBuilder()
                .SetBasePath(System.IO.Directory.GetCurrentDirectory()) //From NuGet Package Microsoft.Extensions.Configuration.Json
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .Build();
                using var servicesProvider = new ServiceCollection()
                 .AddTransient<Runner>() // Runner is the custom class
                 .AddLogging(loggingBuilder =>
                 {
                     // configure Logging with NLog
                     loggingBuilder.ClearProviders();
                     loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                     loggingBuilder.AddNLog(config);
                 }).BuildServiceProvider();

                var runner = servicesProvider.GetRequiredService<Runner>();
                runner.DoAction("Action1");

                Console.WriteLine("Press ANY key to exit");
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                // NLog: catch any exception and log it.
                logger.Error(ex, "Stopped program because of exception");
                throw;
            }
            finally
            {
                LogManager.Shutdown();
            }
            Console.ReadKey();
        }
    }
}

1.6、最簡單的示例

using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;

namespace ConsoleExample
{
    internal static class Program
    {
        private static void Main()
        {
            var logger = LoggerFactory.Create(builder => builder.AddNLog()).CreateLogger<Program>();
            logger.LogInformation("Program has started.");
            Console.ReadKey();
        }
    }
}

2、在ASP.NET CORE專案中使用NLog

2.1、在Web專案中新增包

Install-Package NLog
Install-Package NLog.Web.AspNetCore

2.2、在appsetting.json檔案如下配置

{
  "Logging": {
    "LogLevel": {
      "Default": "Trace",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

2.3、 建立 nlog.config 檔案。

在專案的根目錄中建立 nlog.config(全部小寫)檔案

建立nlog.config檔案,並設定屬性為始終複製,下面是config檔案參考

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Warn"
      internalLogFile="internal-nlog.txt">

  <!-- define various log targets -->
  <targets>
    <!-- write logs to file -->
    <target xsi:type="File" name="allfile" fileName="nlog-all-${shortdate}.log"
                 layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />


    <target xsi:type="File" name="ownFile-web" fileName="nlog-own-${shortdate}.log"
             layout="${longdate}|${logger}|${uppercase:${level}}|  ${message} ${exception}" />

    <target xsi:type="Null" name="blackhole" />
  </targets>

  <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>
</nlog>

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Info"
      internalLogFile="c:\temp\internal-nlog-AspNetCore.txt">

  <!-- enable asp.net core layout renderers -->
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>

  <!-- the targets to write to -->
  <targets>
    <!-- File Target for all log messages with basic details -->
    <target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore-all-${shortdate}.log"
            layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />

    <!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
    <target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore-own-${shortdate}.log"
            layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />

    <!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
    <target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
  </targets>

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

    <!--Output hosting lifetime messages to console target for faster startup detection -->
    <logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />

    <!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) -->
    <logger name="Microsoft.*" maxlevel="Info" final="true" />
    <logger name="System.Net.Http.*" maxlevel="Info" final="true" />
    
    <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
  </rules>
</nlog>

上面的c:\temp可以改成nlog

2.4、program.cs程式碼如下

using NLog;
using NLog.Web;

// Early init of NLog to allow startup and exception logging, before host is built
var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Debug("init main");

try
{
    var builder = WebApplication.CreateBuilder(args);

    // Add services to the container.
    builder.Services.AddControllersWithViews();

    // NLog: Setup NLog for Dependency injection
    builder.Logging.ClearProviders();
    //如果使用Configs資料夾下的配置使用builder.Logging.AddNLog注入
    //builder.Logging.AddNLog("Configs/nlog.config");
    //如果是在根目錄可以直接使用builder.Host.UseNLog();
    builder.Host.UseNLog();

    var app = builder.Build();

    // Configure the HTTP request pipeline.
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Home/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    app.Run();
}
catch (Exception exception)
{
    // NLog: catch setup errors
    logger.Error(exception, "Stopped program because of exception");
    throw;
}
finally
{
    // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
    NLog.LogManager.Shutdown();
}

2.5、 Microsoft日誌記錄過濾器

使用 NLog 5.0 時,預設情況下會忽略 Microsoft 日誌記錄篩選器。只需確保正確配置 NLog 配置規則即可。appsettings.json

<rules>
    <logger name="System.*" finalMinLevel="Warn" />
    <logger name="Microsoft.*" finalMinLevel="Warn" />
    <logger name="Microsoft.Hosting.Lifetime*" finalMinLevel="Info" />
    <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>

寫入日誌

在控制器中注入 ILogger:

using Microsoft.Extensions.Logging;

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
        _logger.LogDebug(1, "NLog injected into HomeController");
    }

    public IActionResult Index()
    {
        _logger.LogInformation("Hello, this is the index!");
        return View();
    }
}

參考文件:

Getting started with .NET Core 2 Console application · NLog/NLog Wiki · GitHub

Getting started with ASP.NET Core 6 · NLog/NLog Wiki · GitHub

https://docs.meowv.com/stack/dotnetcore/nlog-in-dotnet.html

https://zhuanlan.zhihu.com/p/35469359

https://www.cjavapy.com/article/3102/

https://www.cnblogs.com/haiouxiangyun/p/15921375.html

三、使用Serlog

1、控制檯專案

1.1、在專案中新增下面幾個元件包

Install-Package Serilog.Extensions.Logging
Install-Package Serilog
Install-Package Serilog.Sinks.Console
Install-Package Serilog.Sinks.File

1.2、Program.cs程式碼參考如下

using System;
using Serilog;

class Program
{
    static async Task Main()
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .WriteTo.Console()
            .WriteTo.File("logs/myapp.txt", rollingInterval: RollingInterval.Day)
            .CreateLogger();
//上面和下面的都可以        
//           Log.Logger = new LoggerConfiguration()
//          .MinimumLevel.Information()
//          .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
//#if DEBUG
//           .MinimumLevel.Override("Xxx", LogEventLevel.Debug)
//#else
//            .MinimumLevel.Override("Xxx", LogEventLevel.Information)
//#endif
//          .Enrich.FromLogContext()
//          .WriteTo.Console()
//          .WriteTo.File(Path.Combine(Directory.GetCurrentDirectory(), "logs/logs.txt"))
//          .CreateLogger();

        Log.Information("Hello, world!");

        int a = 10, b = 0;
        try
        {
            Log.Debug("Dividing {A} by {B}", a, b);
            Console.WriteLine(a / b);
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Something went wrong");
        }
        finally
        {
            await Log.CloseAndFlushAsync();
        }
    }
}

2、AspNetCore專案

2.1、在專案中新增下面幾個元件包

Install-Package Serilog.AspNetCore
Install-Package Serilog.Sinks.Async
Install-Package Serilog.Sinks.File

2.2、Program.cs程式碼參考:

using Serilog;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

try
{
    Log.Information("Starting web application");

    var builder = WebApplication.CreateBuilder(args);

    builder.Host.UseSerilog(); // <-- Add this line
    
    var app = builder.Build();

    app.MapGet("/", () => "Hello World!");

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}
 //Serilog.Sinks.Http
 //Serilog.Sinks.Seq
 //Log.Logger = new LoggerConfiguration()
 //            .MinimumLevel.Verbose()
 //            .Enrich.WithProperty("ApplicationContext", Program.AppName)
 //            .Enrich.FromLogContext()
 //            .WriteTo.Console()
 //            .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
 //            .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl)
 //            .ReadFrom.Configuration(configuration)
 //            .CreateLogger();

使用的的話直接注入ILogger就可以使用了

參考文件:

官網:https://serilog.net/

官方文件:

控制檯:https://github.com/serilog/serilog/wiki/Getting-Started

web:https://github.com/serilog/serilog-aspnetcore#serilogaspnetcore---

配置:Configuration Basics · serilog/serilog Wiki · GitHub

https://docs.meowv.com/stack/dotnetcore/serilog-in-dotnet.html

https://maomi.whuanle.cn/3.2.serilog.html

https://github.com/serilog/serilog-aspnetcore

https://www.cnblogs.com/ireadme/p/14509704.html

四、自定義日誌提供程式

實現自定義日誌提供程式主要需要實現兩個介面

1、ILogger 支援高效能結構化日誌記錄,輸出日誌的地方,其中log方法是Logger對日誌訊息的寫入實現
2、ILoggerProvider
日誌記錄器提供程式,ILoggerProvider 的實現將透過其 ILoggerProvider.CreateLogger 方法建立 ILogger

需安裝以下Nuget包

  <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
  <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
  <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="8.0.0" />

下面是官方的示例:

有很多日誌記錄提供程式可用於常見日誌記錄需求。 如果某個可用的提供程式不滿足你的應用程式需要,則你可能需要實現自定義的 ILoggerProvider。 在本文中,你將學習如何實現可用於在控制檯中為日誌著色的自定義日誌記錄提供程式。

Docs Github 儲存庫中提供了自定義日誌記錄提供程式示例原始碼。 有關詳細資訊,請參閱 GitHub:.NET Docs - 控制檯自定義日誌記錄

示例自定義記錄器配置

此示例會使用以下配置型別為每個日誌級別和事件 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 實現類別名稱通常是日誌記錄源。 例如,建立記錄器的型別:

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 都有一個唯一的記錄器。 在此實現中,每個日誌級別都需要顯式配置條目才能記錄。

最好是在 ILogger.Log 實現中呼叫 ILogger.IsEnabled,因為 Log 可由任何使用者呼叫,並且不能保證之前已檢查過。 方法 IsEnabled 在大多數實現中應非常快。

C#

TState state,
Exception? exception,

記錄器使用 nameFunc<ColorConsoleLoggerConfiguration> 進行例項化,這將返回當前配置 - 這會處理對透過 IOptionsMonitor.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 中。 此外,還需要 IOptionsMonitor 介面才能更新對基礎 ColorConsoleLoggerConfiguration 物件的更改。

若要控制 ColorConsoleLogger 的配置,請在其提供程式上定義別名:

C#

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

ColorConsoleLoggerProvider 類定義了兩個類範圍的屬性:

  • UnsupportedOSPlatformAttribute"browser" 中不支援 ColorConsoleLogger 型別。
  • ProviderAliasAttribute:配置節可使用 "ColorConsole" 鍵定義選項。

可透過任何有效的配置提供程式指定配置。 請考慮使用以下 appsettings.json 檔案:

JSON

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

這會將日誌級別配置為以下值:

  • LogLevel.Information: ConsoleColor.DarkGreen
  • LogLevel.Warning: ConsoleColor.Cyan
  • LogLevel.Error: ConsoleColor.Red

Information 日誌級別設定為 DarkGreen,這將覆蓋在 ColorConsoleLoggerConfiguration 物件中設定的預設值。

自定義記錄器的使用和註冊

根據約定,在應用程式啟動例程中註冊服務以進行依賴項注入。 註冊在 Program 類中進行,還可能委託給 Startup 類。 本示例將直接從 Program.cs 進行註冊。

若要新增自定義日誌記錄提供程式和相應的記錄器,請從 HostingHostBuilderExtensions.ConfigureLogging(IHostBuilder, Action) 使用 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 建立一個或多個 ILogger 例項。 框架使用 ILogger 例項記錄資訊。

appsettings.json 檔案中的配置會替代以下值:

  • LogLevel.Warning: ConsoleColor.DarkCyan
  • LogLevel.Error: ConsoleColor.DarkRed

按照約定,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;
    }
}

執行此簡單應用程式將把顏色輸出呈現到控制檯視窗,如下圖所示:

Color console logger sample output

自定義日誌功能參考文件:
https://learn.microsoft.com/zh-cn/dotnet/core/extensions/logging-providers

在 .NET 中實現自定義日誌記錄提供程式 - .NET | Microsoft Learn

使用SignalR推送伺服器日誌

https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/logging/?view=aspnetcore-8.0

https://maomi.whuanle.cn/3.1.design_log.html

https://www.cnblogs.com/jackyfei/p/16287326.html

https://www.cnblogs.com/chenyishi/p/18068309

Microsoft.Extensions 探索 / 日誌 Logger - 知乎 (zhihu.com)

.Net Core Logging模組原始碼閱讀 - 李正浩 - 部落格園 (cnblogs.com)

.NET Core下的日誌(2):日誌模型詳解 - Artech - 部落格園 (cnblogs.com)

相關文章