NLog支持以多種不同方式配置,目前同時支持直接編程和使用配置文件兩種方法。本文將對目前支持的各種配置方式作詳細描述。
日志配置
通過在啟動的時候對一些常用目錄的掃描,NLog會嘗試使用找到的配置信息進行自動的自我配置。當你運行一個獨立的*.exe客戶端可執行程序時,NLog將在以下目錄搜索配置信息:
- 標准的程序配置文件(通常為 程序名.exe.config)
- 程序目錄下的程序名.exe.nlog文件
- 程序目錄下的NLog.config文件
- NLog.dll所在目錄下的NLog.dll.nlog文件
- 如果定義了NLOG_GLOBAL_CONFIG_FILE環境變量,則該變量所指向的文件
如果是一個ASP.NET程序,被搜索的目錄包括:
- 標准的web程序配置文件web.config
- 和web.config在同一目錄下的web.nlog文件
- 程序目錄下的NLog.config文件
- NLog.dll所在目錄下的NLog.dll.nlog文件
- 如果定義了NLOG_GLOBAL_CONFIG_FILE環境變量,則該變量所指向的文件
由於.NET Compact Framework不支持程序配置文件(*.exe.config)和環境變量,因此NLog將只會掃描這些地方:
- 程序目錄下的NLog.config文件
- NLog.dll所在目錄下的NLog.dll.nlog文件
- 如果定義了NLOG_GLOBAL_CONFIG_FILE環境變量,則該變量所指向的文件
配置文件格式
NLog支持兩種配置文件格式
- 配置信息嵌入在.NET應用程序標准的*.exe.config或者web.config文件里
- 保存在獨立文件里,也叫單一格式
如果你選擇了第一種方式,使用的是標准的configSections這種機制,那么你的配置文件看起來差不多是這個樣子:
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>
<nlog>
</nlog>
</configuration>
單一格式的配置文件就是一個以<nlog />為根節點的純XMl文件。命名空間並不強制使用,如果使用的話我們就可以利用Visual Studio的智能感應。
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
</nlog>
需要注意的是NLog的配置文件總是大小寫敏感的,不管是在使用的時候,或者即使你沒有使用名字空間。當然只有你的大小寫符合要求,智能感應才能正常工作。
配置元素
下面這些元素可以作為<nlog />的字節點。列表中的前兩個元素在所有的NLog配置文件中都必須提供,其余的則可以選擇使用,通常用於一些復雜場景。
- <targets /> - 定義日志的目標/輸出
- <rules /> - 定義日志的路由規則
- <extensions /> - 從*.dll加載NLog擴展
- <include /> - 導入外部配置文件
- <variable /> - 為配置變量賦值
輸出目標
<target />區域定義了日志的目標或者說輸出。每一個<target />元素代表一個目標。我們需要為每一個目標設置兩個屬性:
- name - 目標的名稱
- type - 目標的類型 - 比如“File”,“Database”,“Mail”。如果你使用了名字空間,這個屬性會被命名為 xsi:type.
除了這兩個屬性,通常來說給目標添加一些其它參數,這些屬性將會影響你在程序中如何使用診斷跟蹤語句(diagnostic traces)。每一個目標都可以有一組不同的參數集合,並且參數都是上下文相關的,你可以在本項目的主頁上找到更多關於參數的詳細說明。Visual Studio的智能感應對這些參數同樣有用。
舉個例子 - “File”目標可以使用“fileName”作為參數來定義輸出文件名,而“Console”目標可以借助“error”參數的值來判斷是否應該把當前進程的diagnostic traces結果輸出到標准錯誤(stderr)還是標准輸出(stdout)控制台。
下面這個例子演示了在<targets />區域同時定義多個目標:兩個files目標,一個network目標和一個OutputDebugString目標:
<targets>
<target name="f1" xsi:type="File" fileName="file1.txt"/>
<target name="f2" xsi:type="File" fileName="file2.txt"/>
<target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>
<target name="ds" xsi:type="OutputDebugString"/>
</targets>
NLog提供了許多已經預先定義好的目標。關於這些目標的詳細說明請參考本項目的主頁。實際上,你也可以很容易的為自己創建目標 - 全部只需大約15-20行代碼即可,更多信息請參考項目文檔。
路由規則
<rules />區域定義了日志的路由規則。實際上它是一個簡單的路由表,對每一個日志源/記錄者的名稱和記錄等級的組合,定義了一個日志寫入目標列表。 表中的規則是被順序處理的。每當遇到匹配的規則時,日志信息就會被送到規則中定義的一個或多個目標去。如果一個規則被標識為最后一個,那么其后的規則都不會被執行。
每一個路由表項就是一個<logger />元素,它的可以接受的屬性有:
- name - 日志源/記錄者的名字 (允許使用通配符*)
- minlevel - 該規則所匹配日志范圍的最低級別
- maxlevel - 該規則所匹配日志范圍的最高級別
- level - 該規則所匹配的單一日志級別
- levels - 該規則所匹配的一系列日志級別,由逗號分隔。
- writeTo - 規則匹配時日志應該被寫入的一系列目標,由逗號分隔。
- final - 標記當前規則為最后一個規則。其后的規則即時匹配也不會被運行。
一些例子:
- <logger name="Name.Space.Class1" minlevel="Debug" writeTo="f1" /> - 名字空間Name.Space下的Class1這個類的所有級別等於或者高於Debug的日志信息都寫入到“f1”這個目標里。
- <logger name="Name.Space.Class1" levels="Debug,Error" writeTo="f1" /> -名字空間Name.Space下的Class1這個類的所有級別等於Debug或Error的日志信息都寫入到“f1”這個目標里。
- <logger name="Name.Space.*" writeTo="f3,f4" /> -名字空間Name.Space下所有類的所有級別的日志信息都寫入到“f3”和“f4”這兩個目標里。
- <logger name="Name.Space.*" minlevel="Debug" maxlevel="Error" final="true" /> - 名字空間Name.Space下所有類的、級別在Debug和Error之間的(包括Debug,Info,Warn,Error) 日志信息都不會被記錄(因為這條規則沒有定義writeTo),同時其它后續規則也都會被忽略(因為這里設置了final="true")。
最簡單的情況下,整個日志的配置信息可以只由一個<target />元素和一個<logger />規則構成,就可以吧一定級別的日志信息路由到期望的目標去。隨着程序不斷的變大,增加新的目標和規則也很簡單。
上下文信息
NLog最棒的功能之一就是使用布局(layouts)的能力。布局由被一個美元符號$加左大括弧“${”和一個右大括弧“}”為標記所包圍的文本所組成。這個標記也就是所謂的“布局生成器(layout renderers),我們可以用它來把一些上下文相關的信息插入到日志信息中。布局可以應用在許多地方,比如可以被用在控制輸出到屏幕或寫入文件信息的格式,也可以用在控制文件名。接下來我們會更多的了解布局的強大。
假設我們希望每個輸出到控制台的信息都包含一些這些信息:
- 當前的日期和時間
- 產生日志信息的類和方法的名字
- 日志等級
- 日志內容
利用Layout來實現很簡單:
<target name="c" xsi:type="Console"
layout="${longdate} ${callsite} ${level} ${message}"/>
或者我們可以把每一個日志記錄者生成的日志信息輸出到一個單獨的文件里:
<target name="f" xsi:type="File" fileName="${logger}.txt"/>
這里我們看到fileName屬性的值被設置為布局生成器${logger},從而使每一條日志信息被寫到一個以日志生成者名字命名的一個文件里。上面這個例子將生成如下一系列文件:
- Name.Space.Class1.txt
- Name.Space.Class2.txt
- Name.Space.Class3.txt
- Other.Name.Space.Class1.txt
- Other.Name.Space.Class2.txt
- Other.Name.Space.Class3.txt
- ...
有一個常見需求是能夠用日期信息來區分日志文件。如果使用${shortdate}布局生成器,這簡直太容易了:
<target name="f" xsi:type="File" fileName="${shortdate}.txt"/>
那么可以給每一個職員生成一個日志文件嗎?答案就是${windows-identity}布局生成器:
<target name="f" xsi:type="File" fileName="${windows-identity:domain=false}.txt"/>
這樣我們就能夠給每一個職員生成一個日志文件了:
- Administrator.txt
- MaryManager.txt
- EdwardEmployee.txt
- ...
更復雜的場景也能做到。下面這個例子說明了如何為每個人每天生成一個日志文件。每天的日志文件存放在不同的文件夾里:
<target name="f" xsi:type="File"
fileName="${shortdate}/${windows-identity:domain=false}.txt"/>
這將創建如下文件:
- 2006-01-01/Administrator.txt
- 2006-01-01/MaryManager.txt
- 2006-01-01/EdwardEmployee.txt
- 2006-01-02/Administrator.txt
- 2006-01-02/MaryManager.txt
- 2006-01-02/EdwardEmployee.txt
- ...
NLog提供了許多預先定義好的布局生成器。關於它們的說明都在這個頁面:http://www.nlog-project.org/layoutrenderers.html。建立你自己的布局生成器也很容易,大概只需要15-20行的代碼而已。詳細做法請參考項目文檔。
包含文件
有時我們希望把配置文件分割為一些比較小的文件。NLog提供了包含文件這一機制來支持這種需求。要包含一個外部文件,你需要做的只是設置fileName這個屬性。同時,和其它大多數NLog配置文件的屬性一樣,fileName也支持用大家都很熟悉的${var}標記引入動態值,這使得我們可以根據環境屬性的不同包含不同的文件。在下面這個例子里,我們總是導入基於當前機器名的配置文件。
<nlog>
...
<include file="${basedir}/${machinename}.config"/>
...
</nlog>
變量的使用可以使我們以比較簡潔的形式書寫復雜或者是重復表達式(如文件名)。定義一個變量的語法是:<variable name="var" value="xxx" />。變量定義好之后,就可以像使用布局生成器一樣 – 通過語法${var}來使用了。下面我們來看一個使用變量的例子:
<nlog>
<variable name="logDirectory" value="${basedir}/logs/${shortdate}"/>
<targets>
<target name="file1" xsi:type="File" fileName="${logDirectory}/file1.txt"/>
<target name="file2" xsi:type="File" fileName="${logDirectory}/file2.txt"/>
</targets>
</nlog>
自動再配置
配置文件在程序啟動時會被自動讀取。然而在一些長時間運行的程序中(比如Windows服務或者ASP.NET程序),有時我們希望能夠在不中斷程序的前提下臨時提高日志的級別。NLog可以一直監視日志配置文件的狀態,並在它們被修改后重新讀取。要激活這一機制,你只需在你的配置文件中設置<nlog autoReload="true" />。注意自動再配置支持引入文件,所以每次如果一個引入文件被修改了,會引起整個配置信息被重新載入。
日志排錯
有時候即使你覺得你已經把日志配置的沒有任何問題了,你的程序就是不輸出任何日志信息。原因可能有很多,最常見的是權限問題,尤其在ASP.NET程序里,aspnet_wp.exe或者w3wp.exe進程可能沒有足夠的權限訪問存放日志文件的目錄。NLog被設計為吃掉任何由於記錄日志而帶來的運行時異常。而下面這些設置可以改變這種行為並/或者重定向這些信息。
- <nlog throwExceptions="true" />- 設置throwExceptions屬性為“true”可以讓NLog不再阻擋這類異常,而是把它們拋給調用者。在部署是這樣做可以幫我們快速定位問題。一旦應用程序已經正確配置了,我們建議把throwExceptions的值設為“false”,這樣由於日志引發的問題不至於導致應用程序的崩潰。
- <nlog internalLogFile="file.txt" />- 設置internalLogFile屬性可以讓NLog把內部的調試和異常信息都寫入指定文件里。
- <nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" /> - 決定內部日志的級別,級別越高,輸出的日志信息越簡潔。
- <nlog internalLogToConsole="false|true" /> - 是否把內部日志輸出到標准控制台。
- <nlog internalLogToConsoleError="false|true" /> - 是否把內部日志輸出到標准錯誤控制台 (stderr)。
異步處理,封裝和復合目標
NLog提供的封裝和復合目標可以修改其它目標的行為,這可以增加一些功能如:
- 異步處理 (被封裝的目標在另一個線程上運行)
- 自動重試 (retry-on-error)
- 負載平衡 (round-robin targets)
- 緩沖 (buffering)
- 過濾 (filtering)
- 備份目標 (災難恢復failover)
- 更多請參考http://www.nlog-project.org/targets.html
定義一個封裝或者復合目標,你只需在一個目標節點里嵌套另一個目標節點即可。你甚至可以封裝一個封裝目標。嵌套的層數沒有任何限制。比如,要給你的配置文件加上異步日志記錄的功能,同時異步日志記錄可以自動重試,你可以這樣做:
<targets>
<target name="n" xsi:type="AsyncWrapper">
<target xsi:type="RetryingWrapper">
<target xsi:type="File" fileName="${file}.txt"/>
</target>
</target>
</targets>
因為異步處理使用的非常普遍,NLog專門為異步處理設計了一個簡化符號,這樣所有需要異步處理的目標都不需要顯式的定義封裝了。你只要設置<targets async="true" />然后你所有的目標就都具備了異步處理的能力了。
缺省封裝
有時我們希望用同一種封裝來處理所有的目標,比如說增加緩沖和/或自動重試功能。NLog為此提供了專門的語法:<default-wrapper />。你只要把這個元素放在<targets />區域里,然后所有的目標都會自動加載同一個封裝目標。需要注意的是<default-wrapper />只對當前這個<targets />區域有效,而你可以使用多個<targets />區域,這樣你就可以把目標分組並用不同的封裝目標處理。
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target name="f1" xsi:type="File" fileName="f1.txt"/>
<target name="f2" xsi:type="File" fileName="f2.txt"/>
</targets>
<targets>
<default-wrapper xsi:type="AsyncWrapper">
<wrapper xsi:type="RetryingWrapper"/>
</default-wrapper>
<target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>
<target name="n2" xsi:type="Network" address="tcp://localhost:4002"/>
<target name="n3" xsi:type="Network" address="tcp://localhost:4003"/>
</targets>
</nlog>
上面的例子里我們定義了兩個緩沖文件目標和三個異步以及自動重試網絡目標。
缺省目標參數
和缺省封裝目標類似,NLog也提供了<default-target-parameters />來讓你為目標參數設置缺省值。比如,如果你不希望日志文件總是被打開,你既可以通過給每一個目標增加 keepFileOpen=”false”屬性來達到這個目的:
<nlog>
<targets>
<target name="f1" xsi:type="File" fileName="f1.txt" keepFileOpen="false"/>
<target name="f2" xsi:type="File" fileName="f2.txt" keepFileOpen="false"/>
<target name="f3" xsi:type="File" fileName="f3.txt" keepFileOpen="false"/>
</targets>
</nlog>
或者,你可以定義一個<default-target-parameters />並把它的值賦給當前<targets />區域里所有的目標。缺省參數時依據不同的類型定義的,並且在XML文件的實際屬性沒有被定義好之前就生效了。
<nlog>
<targets>
<default-target-parameters xsi:type="File" keepFileOpen="false"/>
<target name="f1" xsi:type="File" fileName="f1.txt"/>
<target name="f2" xsi:type="File" fileName="f2.txt"/>
<target name="f3" xsi:type="File" fileName="f3.txt"/>
</targets>
</nlog>