本文將使用一個Nuget的一個組件庫來實現一個簡單強大的日志記錄功能,包采用線程安全實現,所有的記錄在后台完成,即使您在前台調用100萬次方法,耗時也不過1000ms(具體時間依照電腦性能決定),支持日志等級,並提供了一個控件用於分析所有的日志消息。
在visual studio 中的Nuget管理器中可以下載安裝,也可以直接在Nuget控制台輸入下面的指令安裝:
Install-Package HslCommunication
Nuget安裝教程:http://www.cnblogs.com/dathlin/p/7705014.html
聯系作者及加群方式(激活碼在群里發放):http://www.hslcommunication.cn/Cooperation
本組件提供了一個日志功能,實現了常用的3中日志模式:
- 單日志文件模式,日志始終都向該文件寫入,需要程序中自行清除或手動編寫代碼清空,不然文件會很大。
- 根據文件大小存儲的多文件模式,一個日志文件寫入達到某個數值時,創建新文件寫入。
- 根據時間日期的多文件模式,每條日志將根據寫入的時間日志來判斷文件名,比如創建按每天存儲的日志。
我也曾使用過.net中大名鼎鼎的log4net日志組件,一般使用起來確實方便,但是我在諸如實現上述的需求的時候就特別麻煩,而且log4net功能眾多,大多數功能並不是我所需要的,我就需要一個實現txt日志的存儲方式,簡單高效,多種模式即可。於是就自行開發了一個簡單高效的日志組件,並集成到了該通信庫中,本日志組件還提供了一個分析控件及窗口,可以對一個長長的日志文件進行分析統計(只對本組件生成的日志有效),快速定位需要查找的日志信息,支持使用正則表達式來篩選,本日志組件也提供了分級存儲。
Demo程序下載 HslCommunicationDemo.zip
使用之前先要進行命名控件的引用,如下:
using HslCommunication.LogNet;
實例化
首先先實例化一個對象,如果您用於整個application的日志存儲,可以定義成靜態對象。或者直接定義在form窗口的下面也可以。此處先實例化單文件存儲的機制
private ILogNet logNet = new LogNetSingle("D:\\123.txt");
我們通常的做法是日志文件存儲在exe程序目錄下的Logs文件夾中,無論在服務器端還是客戶端都是非常適用的,所以
private ILogNet logNet = new LogNetSingle(Application.StartupPath + "\\Logs\\123.txt");
寫日志
接下來你就可以在窗口的其他地方進行寫入日志了,本組件提供了5個等級的日志寫入功能,名稱參考了常規的日志等級,有 DEBUG , INFO, WARN , ERROR , FATAL ,根據需要進行存儲,還提供了對exception異常的方法支持和自定義的描述化文本寫入,該文本不屬於日志范疇,在日志分析中會被忽略,如下代碼演示幾種不同的寫入(寫入的方式采用統一的接口實現,對於三種日志模式都是適用的):
private void userButton1_Click(object sender, EventArgs e) { // 一般日志寫入 logNet.WriteDebug("調試信息"); logNet.WriteInfo("一般信息"); logNet.WriteWarn("警告信息"); logNet.WriteError("錯誤信息"); logNet.WriteFatal("致命信息"); logNet.WriteException(null, new IndexOutOfRangeException()); // 帶有關鍵字的寫入,關鍵字建議為方法名或是類名,方便分析的時候歸類搜索 logNet.WriteDebug("userButton1_Click", "調試信息"); logNet.WriteInfo("TestForm", "一般信息"); logNet.WriteWarn("隨便什么", "警告信息"); logNet.WriteError("userButton1_Click", "錯誤信息"); logNet.WriteFatal("userButton1_Click", "致命信息"); logNet.WriteException("userButton1_Click", new IndexOutOfRangeException()); }
寫入異常時會被自動賦予 FATAL 等級。寫入效果如下:
下面再說明寫入描述文本的時候發生的事情:
logNet.WriteDescrition("這是一條描述文本");
描述性文本的前面會再新增一行作為明顯的區分。描述性文本不列入到日志分析工具中,會被自動被過濾。
設置等級
為什么要實現日志分級呢?有兩個巨大的好處。第一個好處是方便在日志追述的時候進行篩選,比如我需要查看日志中致命錯誤的時候就尤為重要,可以進行快速的篩選。第二個好處就是用於開發的時候調試使用,有時候我們為了快速的找到程序問題的時候,會對程序運行中各種數據狀態,系統狀態進行輸出,然后分析日志得出結論。當你已經寫了很多很多的
logNet.WriteDebug("調試信息");
代碼后,准備發布使用的時候不想輸出這種僅用於調試的時候的日志,總不可能取消所有的調試日志吧?效率太低,萬一以后需要恢復輸出就麻煩了,所以本日志組件支持寫入日志的時候的等級設置,如果我們把日志的等級設置為INFO級別,那么所有的DEBUG級別的日志都不會存儲,所以我們在發布軟件的時候,只要設置等級就行。
下面再來說明下如何設置日志存儲等級。使用方法:
logNet.SetMessageDegree(HslMessageDegree.DEBUG);//所有等級存儲 logNet.SetMessageDegree(HslMessageDegree.INFO);//除DEBUG外,都存儲 logNet.SetMessageDegree(HslMessageDegree.WARN);//除DEBUG和INFO外,都存儲 logNet.SetMessageDegree(HslMessageDegree.ERROR);//只存儲ERROR和FATAL logNet.SetMessageDegree(HslMessageDegree.FATAL);//只存儲FATAL logNet.SetMessageDegree(HslMessageDegree.None);//不存儲任何等級
選擇上面的一行代碼執行就行。
自定義事件
組件默認存儲所有的等級,如果需要設置,在實例化后即可設置等級。日志組件支持一個事件,在所有的日志進行存儲前(被日志等級過濾掉的不會觸發)會報告事件,可以用於其他操作或是控制台的顯示等等,注意: 如果在事件關聯方法中直接訪問UI線程,會異常
這個事件有個非常大的好處,可以統一輸出顯示你的程序中所有地方的日志,方便統一的控制和處理。
logNet.BeforeSaveToFile += LogNet_BeforeSaveToFile;
private void LogNet_BeforeSaveToFile(object sender, HslEventArgs e) { // 如果需要UI顯示,就要取消注釋下方的代碼 //if(InvokeRequired) //{ // Invoke(new Action(() =>{ // LogNet_BeforeSaveToFile(sender, e); // })); // return; //} string degree = e.HslMessage.Degree.ToString();//獲取等級 DateTime time = e.HslMessage.Time;//獲取時間 string text = e.HslMessage.Text;//日志文本 int threadId = e.HslMessage.ThreadId;//記錄日志的線程id }
這個事件在2019年2月10日 16:36:41上新增了一個額外的功能,就是允許你手動取消當前日志的存儲
private void LogNet_BeforeSaveToFile( object sender, HslEventArgs e ) { e.HslMessage.Cancel = checkBox1.Checked; }
假設這樣設置后,所有的日志都不存儲到文件中去。
private void LogNet_BeforeSaveToFile( object sender, HslEventArgs e ) { if (e.HslMessage.Degree != HslMessageDegree.INFO) { e.HslMessage.Cancel = checkBox1.Checked; } }
如果這么寫,就是只存儲INFO等級的日志信息。
過濾指定的關鍵字日志存儲
有些日志數據可能我們想要觸發BeforeSaveToFile事件,但是不能存儲到日志文件里,那么可以直接按照如下的配置進行設置,會過濾掉存儲到文件中,如下就是過濾掉關鍵字的信息
logNet.FiltrateKeyword( "123" );
按文件大小存儲的實例化
若要按照文件大小進行存儲,例如日志存儲2M后,自動生成新的文件,然后存滿2M后生成新文件,如此重復,則需要指定文件的 存儲路徑 和 大小 ,這種方式存儲的文件名稱不可控制,自動定義為 Logs_20170903170604.txt 的格式,會以當前時間自動命名,如下舉例了實例化一個2M大小的對象:
ILogNet logNet = new LogNetFileSize(Application.StartupPath + "\\customer1", 2 * 1024 * 1024);
只要定義了對象,就可以按照上述寫入日志的代碼來寫了。
按時間日期存儲的實例化
也是一種多文件的存儲機制,和按照大小的存儲非常類似,此處可以配置按照每小時生成新的文件,每天新的文件,每月新的文件,每季度新的文件,每年新的文件,生成的文件名稱也是固定的,需要指定 路徑 和 存儲模式 :
ILogNet logNet = new LogNetDateTime(Application.StartupPath + "\\customer2",GenerateMode.ByEveryHour);//按每小時 ILogNet logNet = new LogNetDateTime(Application.StartupPath + "\\customer2", GenerateMode.ByEveryDay);//按每天 ILogNet logNet = new LogNetDateTime(Application.StartupPath + "\\customer2", GenerateMode.ByEveryMonth);//按每月 ILogNet logNet = new LogNetDateTime(Application.StartupPath + "\\customer2", GenerateMode.ByEverySeason);//按每季度 ILogNet logNet = new LogNetDateTime(Application.StartupPath + "\\customer2", GenerateMode.ByEveryYear);//按每年
單文件模式
單文件的模式在上述已經作為演示說明過了,但是單文件模式提供了兩個額外的方法:
- 獲取該文件日志中所有的內容
- 清空該文件的所有數據
具體的代碼如下所示:
LogNetSingle logNetSingle = logNet as LogNetSingle; if (logNetSingle != null) { string logData = logNetSingle.GetAllSavedLog();//獲取所有的日志信息 logNetSingle.ClearLog();//清除所有的日志信息 }
日志查看器
如果只提供了一個日志的寫入而沒有分析工具,那么本組件就是沒什么競爭力的,本日志組件提供了一個大殺器,日志分析控件!您可以集成到您自己的系統中,該控件只需要接受一個 日志源字符串 (就是日志文件的所有字符串數據,服務器讀取文件發送給遠程客戶端就可以現實遠程查看日志分析!棒不棒!)原先的 ClientServerProject 項目中已經帶有了2個日志查看器,一個服務器端使用了本組件提供的標准form窗口,客戶端使用了日志分析控件實現遠程查看功能,具體代碼可以參照 ClientServerProject 項目的代碼。
功能概述
- 對日志文件中的所有等級日志進行分析,每種等級多少個
- 可以同時根據日志等級和時間區段來篩選日志,比如查看某一時間段的 DEBUG 等級日志
- 可以進行可視化分析,查看日志數據的時間分布情況
- 在可視化的界面,如果某個區間段的某日數量特別高,鼠標移動上去后還可以自動跳轉
此處直接演示一個組件標准的日志查看窗口,這個窗口也可以在wpf項目中顯示:
private void userButton13_Click(object sender, EventArgs e) { // 日志查看器 using (HslCommunication.LogNet.FormLogNetView form = new HslCommunication.LogNet.FormLogNetView()) { form.ShowDialog(); } }
效果上圖
查看日志信息
上圖中點擊了 信息 按鈕后,顯示的圖形發生了變化,只顯示了 信息 等級日志
光標移動到某一區間后,下方會有該區間的日志數量和時間范圍,雙擊后的操作就您自己去發現了。^_^
控件的方式,新建一個窗口,將組件的控件給拖進去,如果這個組件在工具箱不顯示,那么需要將dll文件拖到工具箱。
至於顯示信息代碼:
private void FormLogNetTest_Load(object sender, EventArgs e) { // 該source可以是本地讀取的文件,也可以是網絡發送過來的數據 string source = Encoding.UTF8.GetString(System.IO.File.ReadAllBytes("123.txt")); // 傳入路徑 logNetAnalysisControl1.SetLogNetSource(source); }