如何在 ASP.NET Core 中使用 NLog 的高級特性
NLog 是一個開源的輕量級日志框架,提供了豐富的日志路由和管理功能,同時 NLog 也是非常容易的去配置和擴展,其實在之前的文章中我已經(jīng)討論過了 Nlog,在這篇我準(zhǔn)備繼續(xù)和大家討論一下 NLog 的更多高級功能。
接下來看看如何通過 .config 和 代碼方式 配置 NLog,如何去 輪轉(zhuǎn)日志,如何將 Log 對接 database,如何使用異步的模式提高日志寫入性能,同時我還會分享一些 NLog 的經(jīng)驗技巧。
安裝 NLog
可以通過 NuGet Package Manager 可視化界面 或者 NuGet Package Manager Console 控制臺 安裝以下包文件。
NLog.Web.AspNetCore
NLog.Extensions.Logging
NLog.Config
當(dāng)你安裝完 NLog.Config 之后,有一個叫做 NLog.config 文件會自動引用到你的項目中,值得注意的是,NLog.Config 對 NLog 來說不是唯一的,言外之意就是你即可以用 config 模式配置,也可以用 基于代碼 的模式配置。
使用 .config 文件配置 NLog
NLog 提供了兩種配置方式。
file-based配置模式code-based配置模式
回到剛才的問題,如何采用 file-based 模式,剛才被引入的 NLog.Config 內(nèi)容如下:
<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
??}
如果你想通過編程的方式找到當(dāng)前 NLog 的 target,可使用如下代碼:
var?fileTarget?=?(FileTarget)LogManager.Configuration.FindTargetByName("logfile");
使用 代碼配置 NLog
同樣你也可以使用編碼的形式配置 NLog,只需要調(diào)用 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;
????????}
配置輪轉(zhuǎn)日志
你可以讓 NLog 自動實現(xiàn) 輪轉(zhuǎn)日志,什么叫 輪轉(zhuǎn) 呢?簡單來說就是:你可以讓 Nlog 只保存近 N 個小時的日志 并且自動刪除大于 N 小時的日志,這個特性太實用了,否則的話,你需要經(jīng)常到生產(chǎn)上去刪除日志,下面的代碼展示了如何使用 .config 實現(xiàn)自動輪轉(zhuǎn)日志。
<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>
記錄日志到數(shù)據(jù)庫
創(chuàng)建數(shù)據(jù)庫
你可以使用 NLog 將日志接入到 database 中,下面的腳本用于創(chuàng)建幾張記錄日志的表。
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
數(shù)據(jù)庫連接串和參數(shù)屬性
接下來如何在 NLog 的 target 中指定數(shù)據(jù)庫連接串,請注意 connectionString 和 commandText 是如何配置的。
<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)">
使用參數(shù)化
最后,使用 參數(shù)化查詢 來防止注入攻擊,詳細代碼如下。
<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 文件僅供參考。
<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 將日志記錄到 MySQL,Oracle 和 SQLite 數(shù)據(jù)庫。
使用 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>
你可以實現(xiàn) AsyncWrapper 來實現(xiàn)日志記錄的異步化,詳細配置如下:
??<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 直接進行標(biāo)記為異步化 target,如下配置所示:
<targets?async="true">
??...?Write?your?targets?here?...
targets>
NLog 的最佳實踐
這一小節(jié)列舉了一些使用 NLog 的一些最佳實踐
logger 實例應(yīng)該靜態(tài)化,這樣就可以避免在程序中出現(xiàn)多次 logger 初始化帶來的性能開銷。
利用好 NLog 的 Format(當(dāng)你想結(jié)構(gòu)化日志)支持,避免你自己對 string 的創(chuàng)建和拼接。
指定
throwConfigExceptions="true",可以確保當(dāng) NLog 配置錯誤的時候有詳細的錯誤信息,例子如下:
<nlog?xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"?throwConfigExceptions="true">
當(dāng)記錄完日志后,可以調(diào)用 LogManager.Shutdown()來實現(xiàn)數(shù)據(jù)刷新并且關(guān)閉內(nèi)部所有的線程和定時器。
NLog.LogManager.Shutdown();
不要將
async屬性用在AsyncWrapper之上,否則性能會變慢。慎用 Trace 級別,因為 Trace 會記錄所有的日志,考慮使用 Debug 或者 Info 代替。
NLog 是輕量級并且快速的,可以使用
asynchronous wrappers的方式來提升性能。
關(guān)于 NLog 還是有太多的話要說,NLog 提供了日志的結(jié)構(gòu)化,方便在大量日志上進行快速過濾和分析,在未來的文章中我會討論 NLog 的更多高級特性。
譯文鏈接:https://www.infoworld.com/article/3438540/using-advanced-nlog-features-in-aspnet-core.html
