說實話,我並不是太想寫這篇文章,因為我承諾過要完成博客園的部分功能,所以一直都在積極的利用下班時間來完善這個系統,
但是我又不想讓看我源代碼的朋友不知道我寫的代碼是什么意思,所以我還是單獨寫一個文章,敘述一下我對管理日志的看法,
當然主要是log4net的使用。為了寫這篇文章,我查看了博客園的很多關於log4net的文章,但是結果令我很不滿意,因為他們對於log4net的介紹基本上都是大同小異,
我不敢說他們寫的有錯,因為確實就是那么寫的,但是為什么我還要寫這篇博客呢,因為我想以一個學習者的心態來分析這個框架結構,而不是以一個老師來分析。
從一個學習者到老師需要很多的路要走的,他們都省略了學習者入門時的困難,所以我決定還是來寫一篇博客來敘述一下。希望大家吐槽。
首先,現在系統越來越復雜,並且集成的模塊也越來越多,在測試系統的時候我們可以利用單元測試(有單元測試框架支持,比如nunit等),
還可以通過認為的點擊來測試系統功能的正常,在開發過程中我們可以測試出來很多的問題。但是在系統已經部署到服務器上以后,我們就不能進行單元測試了,
因為部署到服務器上的不是源碼,當然也有其他的方式可以測試系統bug,但是在實際環境中會出現很多令人意想不到的問題。舉個簡單的例子,雖然我們會在系統中進行異常的捕獲,
並且會顯示特定的錯誤信息,但是錯誤信息是對用戶顯示的,我們開發者當然希望知道錯誤的詳細信息,以便我們可以改正錯誤。這是必須的,所以最簡單也最直接的方式就出現了,
就是記錄系統日志,當然如果不采用log4net日志管理框架,我們也是可以通過文件操作來寫入日志,但是這個不便於配置。正如我聽到的一個老師說的,
如果你的系統要進行擴展,那么你會怎么辦?這是當時很令我頭疼的一個問題,但是我現在基本知道思路,就是采用分層開發模式,將不同的層放到對應的類庫中,
另外我們也可以采用IOC(依賴注入)或者Nhibernate等框架來提高系統的靈活性。
廢話太多,大家都會沒有耐心看下去,但是如果這是代碼,我估計大家更不會看下去。呵呵
因為園子里有很多介紹log4net的介紹,所以我就不詳細的介紹了,我只說我個人認為在開發中是重點的,歡迎大家吐槽~~
我們知道log4net有一個姐妹叫log4j,他們是針對.Net和java開發的兩個結構基本相同的框架結構,
看到這種說法,我一直想說,為什么好的框架都會從java移植而來,並且都是apache這個組織開發的呢?這里面也不是我能理解的,我也是吐槽一下。
log4net主要由Appender,Logger,Filter,Layout和一個Object Render組成。
其中
Appender(附着器)主要用來確定日志記錄的位置,也可以這么說就是確定日志是記錄到數據庫還是文件或其他終端。
Logger(記錄器),記錄器 看字面意思就應該可以猜到他就是用來確定要發送的日志信息。比如說,有一個異常發生,我們需要記錄什么數據,都是由Logger負責的。
Filter(過濾器) 可以這么理解,這個和MVC中的Filter還是有差別的,這里的Filter表示的是過濾,就像篩子 可以過濾一樣,而MVC中的Filter也可以叫做過濾器,但是它更多的是完成
Aop(面向切面或面向方面) 的橫向的過濾導向,也可以認為是對某個Action或Controller施加的限制。
Layout(布局) 布局很容易理解,就像裝修一樣,把我們要記錄的數據整理成我們需要的格式。
至於后面的object render,我在官方文章看了一下,個人理解就相當於渲染輸出,就像服務器發送html到瀏覽器以后,瀏覽器要渲染輸出的效果應該是一樣。
簡單的描述了一下log4net的組成,還有很重要的就是log4net的分級,其中log4net一種氛圍5級,按從高到低的順序依次為:
off>fatal>Error>Warn>Info>Debug>All
看到這個分級,大家一定要罵我了,一共有七個值,偏偏說5個分級,這里我解釋一下,off的意思就是關閉log4net的日志輸出,
fatal的級別最高,表示的就是特別嚴重的錯誤,一般是在應用程序崩潰的時候,而error則是表示錯誤出現,一般用在出現了異常以后,
Warn表示警告,這個不會使程序出現異常,但是可能會影響程序性能,info 和debug則可以隨意了。另外all表示的就是所有的分級都可以滿足。
這里大家要注意一下的就是,這個分級是會影響程序日志輸出的。比如說你在程序中定義了 log.Info(message) 和log.debug(message),但是你定義的日志應當輸出的級別是info,很抱歉
的告訴你,log.debug(message)中的信息不會寫入日志,因為debug在順序中是小於info的。
說了log4net中最重要的兩個部分,現在就需要正式的啟用log4net來輸出日志信息了,簽於園友們都有很詳細的配置信息,所以我就把主要的兩個簡單的描述一下,如果有需要我會在注釋中
寫入的。在項目中我們一般會用到輸出日志到文件或者數據庫中,這兩種方式,我個人也非常喜歡這兩種方式。一般我會把日志同時輸出到文件和數據庫中,雖然會有一點的性能損失,但是對於
日志的完整性還是十分值得的,當然輸出的日志越少性能會越高,大家都懂得。
log4net是支持配置文件來更改其輸出方式的,我們可以把配置文件放到項目配置文件web.config或winform的app.config中,也可以單獨的放到固定的配置文件中,現在我們把配置文件放
到項目配置文件中。
配置文件如下
1 <configSections> 2 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/><!--配置一個結點 名稱為log4net--> 3 </configSections> 4 <log4net debug="true"> 5 <appender name="LogFileAppender" type="log4net.Appender.FileAppender" > <!--定義的是日志記錄到文件的附着器 name表示該附着器的名稱-->
<!--在log4net中還有一個附着器RollingFileAppender 它表示會循環生成很多文件,舉例來說,就是設置一共可以生成20個文件,每個文件的大小為2K,那么如果第一個-->、
<!--文件的大小超過2K,就會自動創建一個按順序命名的文件--> 6 <param name="File" value="c:\Log\DBLog.txt" /> <!--日志記錄的存在路徑--> 7 <param name="AppendToFile" value="true" /><!--為true就表示日志會附加到文件,為false,則會重新創建一個新文件--> 8 <layout type="log4net.Layout.PatternLayout"><!--輸出內容控制--> 9 <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" /> 10 </layout> 11 </appender> 12 <appender name="SmtpAppender" type="log4net.Appender.SmtpAppender"><!--設置發送電子郵件的附着器--> 13 <authentication value="Basic" /> 14 <to value="guozhiqi@babaike.com" /> 15 <from value="guozhiqi21@163.com" /> 16 <username value="guozhiqi21" /> 17 <password value="password" /> 18 <subject value="測試錯誤信息顯示" /> 19 <smtpHost value="smtp.163.com" /> 20 <bufferSize value="512" /> 21 <lossy value="true" /> 22 <evaluator type="log4net.Core.LevelEvaluator"> 23 <threshold value="debug"/> 24 </evaluator> 25 <layout type="log4net.Layout.PatternLayout"> 26 <conversionPattern value="%newline%date [%thread] %-5level %logger [%property{NDC}] - %message%newline%newline%newline" /> 27 </layout> 28 </appender> 29 <!--<logger name="smtp"> 30 <level value="debug"/> 31 <appender-ref ref="SmtpAppender"/> 32 </logger>--> 33 <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender"><!--存儲到數據庫的操作--> 34 <bufferSize value="10"/> 35 <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> 36 <connectionString value="server=192.168.1.66;database=教學;user id=sa;password=sa1234"/> 37 <commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date,
@log_thread, @log_level, @log_logger, @log_message, @log_exception)"/> 38 <parameter> 39 <parameterName value="@log_date"/> 40 <dbType value="DateTime"/> 41 <layout type="log4net.Layout.RawTimeStampLayout"/><!--可以認為是記錄日志的時間--> 42 </parameter> 43 <parameter> 44 <parameterName value="@log_thread"/> 45 <dbType value="String"/> 46 <size value="255"/> 47 <layout type="log4net.Layout.PatternLayout"> 48 <conversionPattern value="%thread"/><!--記錄日志時的線程號--> 49 </layout> 50 </parameter> 51 <parameter> 52 <parameterName value="@log_level"/> 53 <dbType value="String"/> 54 <size value="50"/> 55 <layout type="log4net.Layout.PatternLayout"> 56 <conversionPattern value="%level"/><!--日志級別--> 57 </layout> 58 </parameter> 59 <parameter> 60 <parameterName value="@log_logger"/> 61 <dbType value="String"/> 62 <size value="255"/> 63 <layout type="log4net.Layout.PatternLayout"> 64 <conversionPattern value="%logger"/><!--哪個記錄器存儲的該日志--> 65 </layout> 66 </parameter> 67 <parameter> 68 <parameterName value="@log_message"/> 69 <dbType value="String"/> 70 <size value="4000"/> 71 <layout type="log4net.Layout.PatternLayout"> 72 <conversionPattern value="%message"/><!--日志信息--> 73 </layout> 74 </parameter> 75 <parameter> 76 <parameterName value="@log_exception"/> 77 <dbType value="String"/> 78 <size value="255"/> 79 <layout type="log4net.Layout.ExceptionLayout"/><!--異常信息--> 80 </parameter> 81 </appender> 82 <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"><!--這個就是我在上面提到的RolllingFileAppender--> 83 <file value="example.log" /><!--文件名稱--> 84 <appendToFile value="false" /><!--會創建新文件,一般設置為true,這里設置為false,是為了看到創建的文件--> 85 <maximumFileSize value="1KB" /><!--文件大小--> 86 <maxSizeRollBackups value="20" /><!--創建最大文件數--> 87 <layout type="log4net.Layout.PatternLayout"> 88 <conversionPattern value="%level %thread %logger - %message%newline" /> 89 </layout> 90 </appender> 91 <!--<logger name="rolling"> 92 <level value="fatal"/> 93 <appender-ref ref="RollingFile"/> 94 <appender-ref ref="ADONetAppender"/> 95 </logger>--> 96 <root> 97 <level value="info" /> 98 <appender-ref ref="ADONetAppender" /> 99 <appender-ref ref="SmtpAppender"/>--> 100 <appender-ref ref="LogFileAppender"/> 101 <appender-ref ref="ColoredConsoleAppender"/> 102 <appender-ref ref="EventLogAppender"/> 103 <append-ref ref="NetSendAppender"/> 104 <appender-ref ref="RollingFile"/> 105 </root> 106 </log4net>
在配置文件創建好了以后,按道理說就可以在程序中使用log4net了,創建Log4NetController,在該控制器中進行對log4net的測試
1 public class Log4netController : Controller 2 { 3 4 private static readonly log4net.ILog log = log4net.LogManager.GetLogger("rolling"); 5 6 public ActionResult Index() 7 { 8 log4net.Config.XmlConfigurator.Configure(); 9 10 log.Info("log日志信息"); 11 log.Debug("debug信息"); 12 log.Error("error信息"); 13 log.Warn("warn信息"); 14 Exception ex = new Exception("這里顯示的是異常信息"); 15 log.Fatal("fatal信息", ex); 16 return View(); 17 } 18
19 }
運行程序,就可以在數據庫中看到日志生成的記錄
說明log4net的日志記錄到數據庫是正確的。下面我們來分析一下,因為我是在把代碼復制到博客中才加的注釋,所以不是C#標准的注釋方式,
下面我們來把使用log4net中要注意的地方或者我認為是會令人疑惑的地方敘述一下,
-
其實我們在配置log4net的時候最好是直接復制原來可以運行的配置文件,然后進行修改,因為如果你的配置文件有一點錯誤,那么log4net都不會正常工作的,
這里還有一個更嚴重的問題就是我們不會得到log4net引發的任何異常。我們只能是慢慢修改,有耐心,最好是復制 粘貼
-
<!--<logger name="rolling"> 92 <level value="fatal"/> 93 <appender-ref ref="RollingFile"/> 94 <appender-ref ref="ADONetAppender"/> 95 </logger>--> 96 <root> 97 <level value="info" /> 98 <appender-ref ref="ADONetAppender" /> 99 <appender-ref ref="SmtpAppender"/>--> 100 <appender-ref ref="LogFileAppender"/> 101 <appender-ref ref="ColoredConsoleAppender"/> 102 <appender-ref ref="EventLogAppender"/> 103 <append-ref ref="NetSendAppender"/> 104 <appender-ref ref="RollingFile"/> 105 </root>
這段代碼中的root和logger結點我們需要注意一下,因為在root中定義的是針對所有的logger,也就是說如果你在程序中使用的不是LogManager.getLogger(Loggername)這種方式,那么你默認的會使用root結點的配置信息來記錄日志,而如果你使用了這種方式,那么就會使用對應的logger結點的配置信息。
3.log4net是可配置、可修改的靈活日志輸出,我們必須在程序中對log4net進行配置。
log4net.Config.XmlConfigurator.Configure(); 這個是默認的配置方式,當然我們也可以利用參數中加入配置文件路徑來指定配置文件。這個是必須要配置的,切記。
還有就是這個配置應當是在程序啟動時就需要配置完成,因此在application_start中是比較好的方法。
4.log4net輸出日志的時候我們看到了很多的%形式,這是表示的什么意思呢?其實%thread 就是表示當前的線程名稱,%level就是表示日志的級別。
5.最后我還想補充一下,任何和程序執行無關的代碼都會影響程序的性能,日志的輸出和程序功能的完成沒有直接的關系,過多的日志輸出會影響程序響應的性能,另外我們要控制刪除日志的級別,以便不要任何日志信息都會輸出。
6.通過 private static readonly log4net.ILog log = log4net.LogManager.GetLogger("rolling"); 可以的到一個log對象,但是我們如何確定是否需要輸出到對應的級別呢?
log4net提供了
我們可以通過這幾個屬性來判斷是否需要輸出不同級別的日志。
暫時想到的就這么多,我想到別的還會加上,歡迎吐槽
補充一下:
1 <appender name="RollingLogRootFileAppender" type="log4net.Appender.RollingFileAppender"> 2 <!--日志的路徑--> 3 <file value="..\temp\log\Winlog" /> 4 <!--是否覆蓋,默認是追加true--> 5 <appendToFile value="true"/> 6 <!--文件滾動周期(每日創建新日志文件)--> 7 <datePattern value="yyyyMMdd".txt""/> 8 <!--設置無限備份=-1 ,最大備份數為1000--> 9 <maxSizeRollBackups value="1000"/> 10 <!--名稱是否可以更改為false為可以更改--> 11 <staticLogFileName value="false" /> 12 <!--文件滾動選項Composite表示根據日期和大小來滾動--> 13 <rollingStyle value="Composite" /> 14 <layout type="log4net.Layout.PatternLayout"> 15 <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss}[%t][%-5p][%c]%m%n%exception%n" /> 16 </layout> 17 </appender>
這個可以用來對RolingFile進行更好的控制