NLog日誌框架使用探究
前言
日誌是每個程式的基本模組。本文是為了探究如何通過NLog方便及記錄日誌並通過Log4View工具收集日誌統一檢視。
為什麼是NLog?
下載量NLog和Log4Net差不多,這兩個日誌模組是.Net平臺使用最多的兩大日誌模組。
Log4Net上次更新已經是17年3月
NLog更新的比較頻繁,開發者比較活躍,有問題的話修復更及時。
NLog是適用於各種.net平臺(包括.net standard)的靈活而免費的日誌記錄平臺。通過NLog, 可以輕鬆地寫入多個目標。(資料庫、檔案、控制檯), 並動態更改日誌記錄配置。
NLog支援結構化和傳統日誌記錄。NLog的特點: 高效能、易於使用、易於擴充套件和靈活配置。
目的
本文為了探究NLog的使用方式,以及如何通過NLog將日誌統一收集檢視並管理。
配置
NLog可以通過配置方式輕鬆的記錄不同等級,不同結構的日誌。
通過Nuget獲取NLog庫包
Install-Package NLog -Version 4.5.11
下載完後會自動在程式下加入預設的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" xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd" autoReload="true" throwExceptions="false" internalLogLevel="Info" internalLogFile="./logs/nlog-internal.log"> <!-- optional, add some variables https://github.com/nlog/NLog/wiki/Configuration-file#variables --> <!-- See https://github.com/nlog/nlog/wiki/Configuration-file for information on customizing logging rules and outputs. --> <targets> <!-- add your targets here See https://github.com/nlog/NLog/wiki/Targets for possible targets. See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers. --> <!-- Write events to a file with the date in the filename. <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log" layout="${longdate} ${uppercase:${level}} ${message}" /> --> </targets> <rules> <!-- add your logging rules here --> <!-- Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f" <logger name="*" minlevel="Debug" writeTo="f" /> --> <logger name="*" minlevel="Info" writeTo="network" /> <logger name="*" minlevel="Info" writeTo="InfoFile" /> <logger name="*" minlevel="Error" writeTo="ErrorFile" /> </rules> </nlog>
基本配置
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Info"
internalLogFile="./logs/nlog-internal.log">
NLog 根節點以下幾種配置需要注意
autoReload
:配置修改是否自動載入。throwExceptions
:日誌出現異常時是否需要丟擲異常,若配置為true
日誌記錄異常時由於沒有捕獲異常,會導致程式掛掉。internalLogLevel
:表示nlog日誌的執行日誌記錄等級。internalLogFile
:表示nlog日誌的執行日誌記錄的位置。通過./XXXX
的方式可以配置到程式的相對目錄。
日誌等級
Nlog支援以下幾種日誌等級
Level | FirstCharacter | Ordinal |
---|---|---|
Trace | T | 0 |
Debug | D | 1 |
Info | I | 2 |
Warn | W | 3 |
Error | E | 4 |
Fatal | F | 5 |
Off | O | 6 |
在日誌輸入時可以通過${level}
輸入日誌等級,或者通過${level:format=FirstCharacter}
輸出日誌等級的簡寫。
若想檢視所有引數輸出可以到這裡檢視。
輸出例子
Logger logger = NLog.LogManager.GetLogger("test");
logger.Trace("測試test");
logger.Info("測試test");
logger.Warn("測試test");
logger.Error("測試test");
logger.Fatal("測試test");
通過NLog.LogManager.GetLogger
我們可以獲取一個日誌物件示例。傳入的引數為日誌例項名,我們可以在日誌名中通過${logger}
引數輸出日誌例項名。可以將不同的日誌儲存到不同的檔案。
在程式碼中我們不支援Off等級的輸出。
我們輸入到檔案中,輸入配置如下:
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
<logger name="*" minlevel="Debug" writeTo="f" />
目標
NLog通過target配置日誌輸入的目標。可以通過配置多個target將日誌輸入到多個目錄,多個目標(檔案,網路,資料庫等)。
<targets async="true">
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
</targets>
通過將async
設定為true
可以非同步儲存日誌,從而防止日誌影響業務效能。
xsi:type
:輸入型別,支援以下型別。- ColoredConsole : 使用可自定義的顏色將日誌訊息寫入控制檯。
- Console - 將日誌訊息寫入控制檯。
- Debug - 模擬目標-用於測試。
- File - 將日誌訊息寫入一個或多個檔案。
- Mail - 使用 smtp 協議或拾取資料夾通過電子郵件傳送日誌郵件。
- Null - 丟棄日誌訊息。主要用於除錯和基準測試。
name
:目標的名字,可以通過建立Rule
規則限制目標的輸出。fileName
:檔名,日誌儲存檔案時可以儲存到該檔案中。檔名支援引數化,通過各種引數更方便的輸出日誌。layout
:表示輸出的格式,若為最簡單的內容輸入,則直接通過引數設定輸入格式即可。除了最簡單的文字格式還支援以下四種類型的資料,通過xsi:type
引數設定layout
的格式,如xsi:type="JsonLayout"
。- CSV - A specialized layout that renders CSV-formatted events.
- Compound - A layout containing one or more nested layouts.
- JSON - A specialized layout that renders to JSON.
- Log4JXml - A specialized layout that renders Log4j-compatible XML events.
下面列舉兩項常用的輸入方式,檔案輸出和網路輸出。
檔案輸出
通過檔案輸入將日誌儲存到一個或多個檔案。可以通過配置動態進行日誌的儲存。
下面通過json 的格式儲存日誌資訊。
<?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" xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd" autoReload="true" throwExceptions="false" internalLogLevel="Info" internalLogFile="./logs/nlog-internal.log">
<targets>
<target xsi:type="File" name="InfoFile"
fileName="${logDir}/InfoLogs/log.txt"
archiveFileName="${logDir}/InfoLogs/log.{#}.txt"
createDirs="true" keepFileOpen="true" autoFlush="false"
openFileFlushTimeout="10" openFileCacheTimeout="30" archiveAboveSize="10240"
archiveNumbering="Sequence" concurrentWrites="true" encoding="UTF-8">
<layout xsi:type="JsonLayout">
<attribute name="counter" layout="${counter}" />
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message:format=message}" encode="false" />
</layout>
</target>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="InfoFile" />
</rules>
</nlog>
xsi:type
:將檔案型別設定為File
,將日誌儲存到檔案中。fileName
:將日誌檔案儲存到"${logDir}/InfoLogs/log.txt"
。可以通過引數在檔名中加入引數設定。
archiveFileName
:為了防止日誌檔案儲存的太大,我們將日誌檔案拆分儲存。通過archiveFileName
引數設定儲存格式,具體格式可以到這裡檢視。createDirs
:若設定的日誌資料夾不存在,則自動建立資料夾。keepFileOpen
:為了提高檔案寫入效能,避免每次寫入檔案都開關檔案,將keepFileOpen
設定為true,我們通過openFileCacheTimeout
引數定時關閉檔案。autoFlush
:為了提高日誌寫入效能,不必每次寫入日誌都直接寫入到硬碟上,將autoFlush
設定為false,我們通過openFileFlushTimeout
引數定時寫入檔案。openFileCacheTimeout
:將keepFileOpen
引數設定為false,則設定定時關閉日誌。防止日誌一直開著佔用著。openFileFlushTimeout
:將autoFlush
引數設定為false,則設定定時將日誌從快取寫入到硬碟時間。archiveAboveSize
:為了防止一個檔案日誌太大,我們需要根據指定大小將日誌拆檔案儲存。archiveAboveSize
引數的單位是位元組。通過設定為10240=10KB,每個日誌大小達到10KB就會自動拆分檔案,拆分後的檔名規則通過archiveFileName
設定,拆分檔名的規則通過archiveNumbering
設定,具體規則可以檢視[這裡]。concurrentWrites
:支援多個併發一起寫檔案,提高檔案寫入效能。encoding
: Nlog預設儲存的編碼格式為Encoding.Default
,中文儲存到日誌中會出現亂碼,將其設定為utf-8
,就可以正常儲存了。
我們可以在targets節點下增加多個target,用於輸出多中目標。
當我們開啟非同步記錄日誌時,同時設定了保持檔案開啟,且設定了快取時間,若在時間內超過了日誌大小,並不會立即分檔案,而是在檔案關閉後才會進行分檔案。
Json格式儲存
<layout xsi:type="JsonLayout">
<attribute name="counter" layout="${counter}" />
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message:format=message}" encode="false" />
在target中將layout的 xsi:type
設定為JsonLayout
儲存為Json格式。
Json格式儲存我們需要在layout
節點下增加attribute
來增加欄位。上面增加了四個欄位。
counter
:行數,行數表示日誌當前記錄的行數。time
:時間,可以通過引數儲存自己想要的時間格式。level
:日誌等級,當前記錄的日誌等級。message
:資訊,記錄的資訊,若需要記錄中文,則需要設定`encode="false",否則json格式會自動將json的中文內容儲存為unicode編碼。
具體的其他Json引數可以看這裡
多目標
<targets>
<target xsi:type="File" name="InfoFile" fileName="${logDir}/InfoLogs/log.txt" archiveFileName="${logDir}/InfoLogs/log.{#}.txt" createDirs="true" keepFileOpen="true" autoFlush="false"
openFileFlushTimeout="10" openFileCacheTimeout="30"
archiveAboveSize="10240" archiveNumbering="Sequence" concurrentWrites="true" encoding="UTF-8">
<layout xsi:type="JsonLayout">
<attribute name="counter" layout="${counter}" />
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message:format=message}" encode="false" />
</layout>
</target>
<target xsi:type="File" name="ErrorFile" fileName="${logDir}/ErrorLogs/log.txt"
archiveFileName="${logDir}/ErrorLogs/log.{#}.txt" createDirs="true" keepFileOpen="true" autoFlush="false"
openFileFlushTimeout="10" openFileCacheTimeout="30"
archiveAboveSize="10240" archiveNumbering="Sequence"
concurrentWrites="true" encoding="UTF-8">
<layout xsi:type="JsonLayout">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" encode="false" />
<attribute name="exception">
<layout xsi:type="JsonLayout">
<attribute name="callsite" layout="${callsite}" />
<attribute name="callsite-linenumber" layout="${callsite-linenumber} " />
</layout>
</attribute>
</layout>
</target>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="InfoFile" />
<logger name="*" minlevel="Error" writeTo="ErrorFile" />
</rules>
我們可以在<targets>
節點下增加多個target增加多個輸出目標,我們通過設定2個目標,將info和error日誌分開儲存。其中很多引數是共用的,我們可以設定一個預設引數default-target-parameters
,減少配置檔案節點。
<default-target-parameters xsi:type="File"
createDirs="true"
keepFileOpen="true" autoFlush="false" openFileFlushTimeout="10" openFileCacheTimeout="30"
archiveAboveSize="10240" archiveNumbering="Sequence" concurrentWrites="true" encoding="UTF-8"/>
通過以上設定,這些設計的節點就被設定為預設值,簡化後的配置檔案如下。
<target>
<default-target-parameters xsi:type="File" createDirs="true" keepFileOpen="true" autoFlush="false" openFileFlushTimeout="10" openFileCacheTimeout="30" archiveAboveSize="10240" archiveNumbering="Sequence" concurrentWrites="true" encoding="UTF-8"/>
<target xsi:type="File" name="InfoFile" fileName="${logDir}/InfoLogs/log.txt" archiveFileName="${logDir}/InfoLogs/log.{#}.txt">
<layout xsi:type="JsonLayout">
<attribute name="counter" layout="${counter}" />
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message:format=message}" encode="false" />
</layout>
</target>
<target xsi:type="File" name="ErrorFile" fileName="${logDir}/ErrorLogs/log.txt" archiveFileName="${logDir}/ErrorLogs/log.{#}.txt">
<layout xsi:type="JsonLayout">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" encode="false" />
<attribute name="exception">
<layout xsi:type="JsonLayout">
<attribute name="callsite" layout="${callsite}" />
<attribute name="callsite-linenumber" layout="${callsite-linenumber} " />
</layout>
</attribute>
</layout>
</target>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="InfoFile" />
<logger name="*" minlevel="Error" writeTo="ErrorFile" />
</rules>
引數
在Nlog節點下加入variable節點可以建立自定義引數。
<variable name="logDir" value="${basedir}/logs/${logger:shortName=true} /${shortdate}"/>
name
:表示引數名。value
:表示引數值。
引數設定完後就可以通過${name}
的方式獲取引數值。
Nlog已定義的一些引數可以到檢視
規則
通過target我們可以自定義輸出方式。同時我們可以建立一系列規則約束輸出的內容。
<rules>
<logger name="*" minlevel="Debug" writeTo="f" />
</rules>
在Nlog
節點下新增rules
節點,rules
節點下可以配置多個logger
節點,每個logger
節點即為一條約束。
name
:logger名稱,若為*
則表示適用於所有日誌,若我們某個target
專門用於logdemo.test
類的日誌輸出,則那麼可以設定為logdemo.test.*
,表示當前約束只允許名稱空間為logdemo.test
開頭的日誌輸出。minlevel
:表示當前約束的最小等級,只有等於或大於該值的日誌等級才會被記錄。writeTo
:表示當前規則約束哪個target
。
更多其他規則引數可以看這裡
日誌分發
通過以上設定,我們可以通過各種targets
將日誌存放到不同地方,通過rules
指定儲存不同等級的日誌。日誌本地檔案存放主要是用於進行系統排查錯誤用的,有時候我們可能希望將日誌合併存放或檢視。NLog本身就支援通過Tcp或Udp將日誌分發到其他地方。
我們在儲存檔案的同時只需要新增一條target
,同時將其型別設定為Network
,在通過設定rules
對其進行必要約束就可以將日誌分發到其他地方。由於我們前面設定了async
引數非同步儲存日誌,因此網路好壞並不會影響我們業務處理時效。
<?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" xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd" autoReload="true" throwExceptions="false" internalLogLevel="Info" internalLogFile="./logs/nlog-internal.log">
...
<targets async="true">
...
</targets>
<targets async="true">
<target xsi:type="Network" address="udp://127.0.0.1:878" name="network" newLine="false" maxMessageSize="65000" encoding="gbk" layout="${log4jxmlevent:includeCallSite=true:includeNLogData=true}"/>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="network" />
...
</rules>
</nlog>
為了和檔案區分,我們我們新增了一個targets
專門用於Network。
xsi:type
:通過設定型別為Network
表示通過網路傳輸日誌。address
:設定地址格式為協議://ip:埠
。maxMessageSize
:表示最大傳輸訊息大小,預設為65000。newLine
:表示日誌訊息末尾追加換行符。encoding
:表示日誌傳輸的編碼,預設為UTF-8,中文需要設定為GBK編碼,否則在對端可能會出現亂碼的情況。
具體完整引數可以看這裡
通過以上設定,就可以將日誌傳送到指定地址了,通過Log4JXml格式傳送到對端。
日誌收集
我們通過Log4View收集日誌進行檢視。目前官網最新的是Log4View2版本,有30天的免費使用時間,30天自動變為社群版本,依然可以免費使用,但是想使用一些高階功能則需要付費使用。
Log4View支援Nlog和Log4Net,同時支援查詢,過濾等功能。
從官網下載後需要進行安裝,目前Log4View不支援中文。
Log4View2支援多種目標的檔案輸入,可以通過檔案,資料庫或網路等途徑輸入日誌。
開啟後介面如圖所示
在File-Receiver新增一個接收者
通過以上設定即可在Log4View接收資料了。我們傳送幾條訊息
Logger logger = NLog.LogManager.GetLogger("test");
logger.Trace("測試test");
logger.Info("測試test");
logger.Warn("測試test");
logger.Error("測試test");
logger.Fatal("測試test");
try
{
throw new Exception("錯誤test");
}
catch (Exception exception)
{
logger.Error(exception);
}
由於我們設定的日誌等級為Info,因此Trace等級的日誌不會傳輸過來。
在介面左下角可以新增過濾器,支援多種篩選模式。