1. 程式人生 > >NLog日誌框架使用探究

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可以非同步儲存日誌,從而防止日誌影響業務效能。

  1. xsi:type:輸入型別,支援以下型別。
    • ColoredConsole : 使用可自定義的顏色將日誌訊息寫入控制檯。
    • Console - 將日誌訊息寫入控制檯。
    • Debug - 模擬目標-用於測試。
    • File - 將日誌訊息寫入一個或多個檔案。
    • Mail - 使用 smtp 協議或拾取資料夾通過電子郵件傳送日誌郵件。
    • Null - 丟棄日誌訊息。主要用於除錯和基準測試。
  2. name:目標的名字,可以通過建立Rule規則限制目標的輸出。
  3. fileName:檔名,日誌儲存檔案時可以儲存到該檔案中。檔名支援引數化,通過各種引數更方便的輸出日誌。
  4. 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>
  1. xsi:type:將檔案型別設定為File,將日誌儲存到檔案中。
  2. fileName:將日誌檔案儲存到"${logDir}/InfoLogs/log.txt"

    可以通過引數在檔名中加入引數設定

  3. archiveFileName:為了防止日誌檔案儲存的太大,我們將日誌檔案拆分儲存。通過archiveFileName引數設定儲存格式,具體格式可以到這裡檢視。
  4. createDirs:若設定的日誌資料夾不存在,則自動建立資料夾。
  5. keepFileOpen:為了提高檔案寫入效能,避免每次寫入檔案都開關檔案,將keepFileOpen設定為true,我們通過openFileCacheTimeout引數定時關閉檔案。
  6. autoFlush:為了提高日誌寫入效能,不必每次寫入日誌都直接寫入到硬碟上,將autoFlush設定為false,我們通過openFileFlushTimeout引數定時寫入檔案。
  7. openFileCacheTimeout:將keepFileOpen引數設定為false,則設定定時關閉日誌。防止日誌一直開著佔用著。
  8. openFileFlushTimeout:將autoFlush引數設定為false,則設定定時將日誌從快取寫入到硬碟時間。
  9. archiveAboveSize:為了防止一個檔案日誌太大,我們需要根據指定大小將日誌拆檔案儲存。archiveAboveSize引數的單位是位元組。通過設定為10240=10KB,每個日誌大小達到10KB就會自動拆分檔案,拆分後的檔名規則通過archiveFileName設定,拆分檔名的規則通過archiveNumbering設定,具體規則可以檢視[這裡]。
  10. concurrentWrites:支援多個併發一起寫檔案,提高檔案寫入效能。
  11. 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等級的日誌不會傳輸過來。

在介面左下角可以新增過濾器,支援多種篩選模式。



參考文件

  1. 框架學習與探究之日誌元件--Log4Net與NLog
  2. NLog基本介紹
  3. 一個簡單好用的日誌框架NLog
  4. NLog tutorial
  5. Advanced NLog Configuration file
  6. Log4ViewHelp