如何在 ASP.NET Core 中使用 NLog 的高級特性

NLog 是一個開源的輕量級日誌框架,提供了豐富的日誌路由和管理功能,同時 NLog 也是非常容易的去配置和擴展,其實在之前的文章中我已經討論過了 Nlog,在這篇我準備繼續和大家討論一下 NLog 的更多高級功能。

接下來看看如何通過 .config代碼方式 配置 NLog,如何去 輪轉日誌,如何將 Log 對接 database,如何使用異步的模式提高日誌寫入性能,同時我還會分享一些 NLog 的經驗技巧。

安裝 NLog

可以通過 NuGet Package Manager 可視化界面 或者 NuGet Package Manager Console 控制檯 安裝以下包文件。

  • NLog.Web.AspNetCore

  • NLog.Extensions.Logging

  • NLog.Config

當你安裝完 NLog.Config 之後,有一個叫做 NLog.config 文件會自動引用到你的項目中,值得注意的是,NLog.Config 對 NLog 來說不是唯一的,言外之意就是你即可以用 config 模式配置,也可以用 基於代碼 的模式配置。

使用 .config 文件配置 NLog

NLog 提供了兩種配置方式。

  • file-based 配置模式

  • code-based 配置模式

回到剛纔的問題,如何採用 file-based 模式,剛纔被引入的 NLog.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">
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>
  <targets>
    <target name="logfile" xsi:type="File" fileName="D:\logs\LogMessages-${shortdate}.log" />
  </targets>
  <rules>
    <logger name="*" minlevel="Trace" writeTo="logfile" />
  </rules>
</nlog>

下面的代碼展示瞭如何在 Controller 下用 NLog 記錄日誌。


 public class HomeController : Controller
 {
     Logger _logger = (Logger)LogManager.GetCurrentClassLogger(typeof(Logger));  

     public IActionResult Index()
      {
         _logger.Info("Application started");       
          return View();
      }
    //Other action methods
  }

如果你想通過編程的方式找到當前 NLog 的 target,可使用如下代碼:


var fileTarget = (FileTarget)LogManager.Configuration.FindTargetByName("logfile");

使用 代碼配置 NLog

同樣你也可以使用編碼的形式配置 NLog,只需要調用 NLog 提供的 API 接口即可,下面的代碼展示瞭如何配置 Nlog。


        private static void ConfigureNLog()
        {
            var logConfiguration = new LoggingConfiguration();
            
            var dbTarget = new DatabaseTarget();
            dbTarget.ConnectionString = "Data Source=JOYDIP;initial catalog=NLogDemo;User Id=sa;Password=sa1@3#.;"; dbTarget.CommandText = "INSERT INTO DbLog (level, callsite, message, logdatetime)" +" Values(@level, @callsite, @message, @logdatetime)";
            dbTarget.Parameters.Add(new DatabaseParameterInfo("@level", "${level}"));
            dbTarget.Parameters.Add(new DatabaseParameterInfo("@callSite", "${callSite}"));
            dbTarget.Parameters.Add(new DatabaseParameterInfo("@message", "${message}"));
            dbTarget.Parameters.Add(new DatabaseParameterInfo("@logdatetime","${date:s}"));

            var rule = new LoggingRule("*", LogLevel.Debug, dbTarget);

            logConfiguration.LoggingRules.Add(rule);
            LogManager.Configuration = logConfiguration;
        }

配置輪轉日誌

你可以讓 NLog 自動實現 輪轉日誌,什麼叫 輪轉 呢?簡單來說就是:你可以讓 Nlog 只保存近 N 個小時的日誌 並且自動刪除大於 N 小時的日誌,這個特性太實用了,否則的話,你需要經常到生產上去刪除日誌,下面的代碼展示瞭如何使用 .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">
   <targets>
    <target name="logfile"
            xsi:type="File"
            fileName="${basedir}/logs/App.log"
            layout="${longdate}  ${message}"
            archiveFileName="${basedir}/logs/archive.{#}.log"
            archiveEvery="Day"
            archiveNumbering="Rolling"
            maxArchiveFiles="7"
            concurrentWrites="true"
            keepFileOpen="true" />
  </targets>
  <rules>
    <logger name="*" minlevel="Info" writeTo="logfile" />
  </rules>
</nlog>

記錄日誌到數據庫

創建數據庫

你可以使用 NLog 將日誌接入到 database 中,下面的腳本用於創建幾張記錄日誌的表。


CREATE TABLE [dbo].[DbLog](
      [Id] [int] IDENTITY(1,1) NOT NULL,
      [Level] [varchar](max) NULL,
      [CallSite] [varchar](max) NULL,
      [Message] [varchar](max) NULL,
      [AdditionalInfo] [varchar](max) NULL,
      [LogDateTime] [datetime] NOT NULL,
 CONSTRAINT [PK_DbLogs] PRIMARY KEY CLUSTERED
(
      [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

數據庫連接串和參數屬性

接下來如何在 NLog 的 target 中指定數據庫連接串,請注意 connectionStringcommandText 是如何配置的。


<target name="database" xsi:type="Database" keepConnection="true"
 useTransactions="true"
 dbProvider="System.Data.SqlClient"
 connectionString="data source=localhost;initial
 catalog=NLogDemo;integrated security=false;
 persist security info=True;User ID=sa;Password=sa1@3#."
 commandText="INSERT INTO DbLog (level, callsite, message, additionalInfo,
 logdatetime) Values (@level, @callsite, @message, @additionalInfo,
 @logdatetime)">

使用參數化

最後,使用 參數化查詢 來防止注入攻擊,詳細代碼如下。


<parameter name="@level" layout="${level}" />
<parameter name="@callSite" layout="${callsite}" />
<parameter name="@message" layout="${message}" />
<parameter name="@additionalInfo" layout="${var:AdditionalInfo}" />
<parameter name="@logdatetime" layout="${date:s}" />

完整的 NLog

以下是完整的 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">
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>
  <variable name="AdditionalInfo" value=""/> 
  <targets>
    <target name="database" xsi:type="Database" keepConnection="true"
    useTransactions="true"
    dbProvider="System.Data.SqlClient"
    connectionString="data source=localhost;initial
    catalog=NLogDemo;integrated security=false;persist security  
    info=True;User ID=sa;Password=sa1@3#."
    commandText="INSERT INTO DbLog
    (level, callsite, message, additionalInfo, logdatetime)
    Values (@level, @callsite, @message, @additionalInfo, @logdatetime)">
      <parameter name="@level" layout="${level}" />
      <parameter name="@callSite" layout="${callsite}" />
      <parameter name="@message" layout="${message}" />
      <parameter name="@additionalInfo" layout="${var:AdditionalInfo}" />
      <parameter name="@logdatetime" layout="${date:s}" />
    </target>       
  </targets>
  <rules>
    <logger levels="Debug,Info,Error,Warn,Fatal" name="databaseLogger" writeTo="database"/>
  </rules>
</nlog>

除了 SQL Server 之外,還可以使用 NLog 將日誌記錄到 MySQLOracleSQLite 數據庫。

使用 AsyncWrapper 提高性能

NLog 支持多種 targets,比如:AsyncWrapper, BufferingWrapper, FallbackGroup 和 RetryingWrapper,異步的 target 爲了提升性能採用 消息的隊列化 並在多個線程中提取隊列消息,下面的代碼展示瞭如何使用 AsyncWrapper


<targets>
  <target xsi:type="AsyncWrapper"
          name="String"
          queueLimit="Integer"
          timeToSleepBetweenBatches="Integer"
          batchSize="Integer"
          overflowAction="Enum">
    <target ... />
  </target>
</targets>

你可以實現 AsyncWrapper 來實現日誌記錄的異步化,詳細配置如下:


  <targets>
    <target name="asyncFile" xsi:type="AsyncWrapper">
      <target xsi:type="File" name="fileLog"
         fileName="${basedir}/Logs/${shortdate}.log"
            layout="${longdate} ${uppercase:${level}} ${message}"/>
    </target>
  </targets>
  <rules>
    <logger levels="Debug,Info,Error,Warn,Fatal" writeTo="asyncFile"/>
  </rules>

除了這種方式,你還可以在所有的 targets 上用 async=true 直接進行標記爲異步化 target,如下配置所示:


<targets async="true">
  ... Write your targets here ...
</targets>

NLog 的最佳實踐

這一小節列舉了一些使用 NLog 的一些最佳實踐

  • logger 實例應該靜態化,這樣就可以避免在程序中出現多次 logger 初始化帶來的性能開銷。

  • 利用好 NLog 的 Format(當你想結構化日誌)支持,避免你自己對 string 的創建和拼接。

  • 指定 throwConfigExceptions="true" ,可以確保當 NLog 配置錯誤的時候有詳細的錯誤信息,例子如下:


<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true" throwConfigExceptions="true">

  • 當記錄完日誌後,可以調用 LogManager.Shutdown() 來實現數據刷新並且關閉內部所有的線程和定時器。


NLog.LogManager.Shutdown();

  • 不要將 async 屬性用在 AsyncWrapper 之上,否則性能會變慢。

  • 慎用 Trace 級別,因爲 Trace 會記錄所有的日誌,考慮使用 Debug 或者 Info 代替。

  • NLog 是輕量級並且快速的,可以使用 asynchronous wrappers 的方式來提升性能。

關於 NLog 還是有太多的話要說,NLog 提供了日誌的結構化,方便在大量日誌上進行快速過濾和分析,在未來的文章中我會討論 NLog 的更多高級特性。

譯文鏈接:https://www.infoworld.com/article/3438540/using-advanced-nlog-features-in-aspnet-core.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章