萬惡的加班還在延續着,分析軟件日志分析的頭疼還是沒有能夠找到問題的症結所在。 五十多兆的日志文件中,很多都是沒用的,有用的信息都被這些無用的信息給推攘到了不知名的角落里。我愣是找了一個小時,找到的有用的信息寥寥無幾,抬頭望望遠處,已經感覺到有些眼暈了。 考慮到每天都要進行這樣的診斷工作,於是決定寫一個日志分析的小軟件,要求能夠過濾掉帶有指定關鍵字的行,並且能夠高亮某些關鍵字。於是,LogAnalysiser這個小工具誕生了。
程序運行效果圖
下面是具體的截圖:
啟動界面(采用了Slash窗體,在程序啟動時會自動檢測缺失的配置文件或者程序集):
然后啟動到主界面(主界面包含了配置窗體,着色窗體,更新窗體):
這個是配置窗體,多個關鍵字或者子句,利用豎線分隔開,程序會自動過濾掉含有這些關鍵字的文本行:
下面的這個是着色窗體,輸入關鍵字,以數顯隔開,點擊確定按鈕可以實時實現關鍵字高亮:
下面這個是更新窗體,主要負責軟件更新工作:
然后這里是幫助文檔:
這就是這個軟件的大概,雖然很小,但是算是比較的全面。
下面來說下在制作過程中使用到的技術:
技術一: 異步操作(采用APM模式)
關於這個模式的具體講解,可以參見我之前的博客文章:我所知道的.net異步
在軟件Slash窗體加載,關鍵字過濾以及軟件更新的時候,由於這三個操作比較耗時,所以采用了異步方式來進行,即使用BeginInvoek和與之配對的EndInvoke方式來達到目的。 比如說軟件中的LoadAppendingText()函數主要是用來循環過濾關鍵字來達到簡化日志的目的,一旦日志文件體積非常大的情況下,這個函數將會阻塞主界面,導致假死狀況。針對這種情況,我利用異步方式來處理,也就是利用下面代碼進行了封裝,從而產生異步效果:
#region Begin and End Invoke of Async mode /// <summary> /// 異步開始 /// </summary> private void BeginInvokeAppending() { Action action = new Action(LoadAppendingText); IAsyncResult result = action.BeginInvoke(new AsyncCallback(EndInvokeAppending),action); pPrograss.Maximum = GetTotalCounts(); tTick.Enabled = true; } /// <summary> /// 異步結束 /// </summary> /// <param name="iar"></param> private void EndInvokeAppending(IAsyncResult iar) { btnAnalysis.Invoke(new Action(delegate { btnAnalysis.Enabled = false; })); tTick.Enabled = false; notificationIcon.Image = (Image)WinRes.Complete; Action action = (Action)iar.AsyncState; action.EndInvoke(iar); } #endregion
這樣,當軟件運行的時候,界面不會卡死,一切都很流暢:
所以,從上面的異步方式看來,這種模式下,我們只需要對耗時函數利用BeginInvoke和EndInvoke進行一下簡單的封裝即可,省時也省力。
需要說明的是,利用異步和界面交互,不得不遇到一個跨線程的問題,不過我們可以通過Form控件的Invoke方式來進行,也就是類似如下的操作:
lblStatus.Invoke(new Action(delegate { lblStatus.Text = "更新完畢。"; }));
技術二: 委托事件傳值。
關於委托的更多詳細情況,請參見我之前的博客:淺談C#中常見的委托
在制作本軟件的過程中,着色的字體需要實時的顯示;Slash窗體檢測完畢,也需要傳值給主窗體,然后自己關閉掉。 這兩個地方都使用了委托事件來進行,具體怎么用呢,請看下面的步驟:
首先,聲明全局委托:
/// <summary> /// 全局委托,用於着色 /// </summary> /// <param name="text">待着色文本</param> public delegate void ColorDaemonDelegate(string text);
然后再DaemonFrm窗體中(也就是進行着色配置的窗體中),聲明一個OnColorDaemonEventHandler事件,用於拋出通知:
public event ColorDaemonDelegate OnColorDaemonEventHandler;
那么,這個通知如何拋出呢?
當然是在點擊着色按鈕的時候拋出去,它向外界宣布:我現在要着色啦,於是它在以下的代碼中將着色事件拋了出去:
private void btnColor_Click(object sender, EventArgs e) { string text = txtWordDaemon.Text; OnColorDaemonEventHandler(text); //拋出事件 }
可以看出,這個事件拋出的時候,帶有一個參數,這個參數就是需要高亮的關鍵字。 那么事件拋出來了,拋給誰了?誰接收到了呢? 之后的內容估計就是我們非常常見的了,即事件注冊:
/// <summary> /// 點擊主窗體中的着色按鈕,可以對當前文檔進行關鍵字高亮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void tsBtnColor_Click(object sender, EventArgs e) { if (daemonFrm == null || daemonFrm.IsDisposed == true) { daemonFrm = new DaemonFrm(); } daemonFrm.OnColorDaemonEventHandler += new ColorDaemonDelegate(daemonFrm_OnColorDaemonEventHandler); daemonFrm.Show(); }
利用上面的+=號,就把剛才拋出的事件給接住了,並且這個拋出的事件被主窗體給接住了。
下面是針對這個拋出的事件進行處理:
/// <summary> /// 着色委托事件,可以實時高亮關鍵字 /// </summary> /// <param name="text"></param> private void daemonFrm_OnColorDaemonEventHandler(string text) { RichTextBoxEx.SetColorBox(richTextBox1,richTextBox1.Text, text, Color.Red); }
上面的RichTextBoxEx.SetColorBox是一個利用擴展方法實現的函數,就可以實現關鍵字的實時高亮,看看效果:
這樣就可以非常方便的分析日志了。
技術三:更新組件的編寫。
更新組件是軟件最常用的組件之一,本軟件的更新組件主要采用HttpWebRequest和HttpWebResponse進行數據獲取並結合異步機制完成。
首先,來看看下載數據的函數:
private void DownLoadVersion(string url,string filename,ProgressBar progress,Label label) { int percent = 0; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); request.ContentType = @"application/octet-stream"; request.Credentials = CredentialCache.DefaultCredentials; HttpWebResponse response = (HttpWebResponse)request.GetResponse(); long totalBytes = response.ContentLength; //獲取文件字節數 progress.Invoke(new Action(delegate { progress.Maximum = (int)totalBytes; })); Stream responseStream = response.GetResponseStream(); //保存到內存 Stream fileStream = new FileStream(filename, FileMode.Create); long totalDownloadBytes = 0; byte[] bytes = new byte[1024]; int paragraphByteSize = responseStream.Read(bytes, 0, (int)bytes.Length); //一次性讀取1024個字節 while (paragraphByteSize > 0) { totalDownloadBytes += paragraphByteSize; //當前已經讀取的字節數 fileStream.Write(bytes, 0, paragraphByteSize); //寫入到文件 progress.Invoke(new Action(delegate { progress.Value = (int)totalDownloadBytes; })); paragraphByteSize = responseStream.Read(bytes, 0, (int)bytes.Length); //繼續讀取下一段 percent = (int)((float)totalDownloadBytes / (float)totalBytes * 100); //進度百分比 label.Invoke(new Action(delegate { label.Text = "當前已經更新:" + percent.ToString() + "%"; })); } responseStream.Close(); fileStream.Close(); }
這里我已經做了不少的注釋了,其主體的邏輯就是得到請求數據,然后1字節1字節的寫入,直到下載完畢為止。
如果直接運行這個函數進行更新的話,會造成界面假死,所以在這里我采用了和之前一樣的異步處理方式,即利用BeginInvoke和EndInvoke方式來進行。這樣就保證了界面的流暢性。
/// <summary> /// 開始進行異步更新 /// </summary> /// <param name="url">軟件地址</param> /// <param name="filename">軟件名稱</param> /// <param name="progress">PrograssBar進度條</param> /// <param name="label">Label狀態標簽</param> private void BeginDownload(string url,string filename,ProgressBar progress,Label label) { //利用Action委托進行代理 Action<string, string, ProgressBar, Label> action = new Action<string, string, ProgressBar, Label>(DownLoadVersion); //開始進行異步 action.BeginInvoke(url, filename, progress, label, new AsyncCallback(EndDownload), action); } /// <summary> /// 異步更新結束 /// </summary> /// <param name="iar">異步狀態</param> private void EndDownload(IAsyncResult iar) { //還原對象 Action<string, string, ProgressBar, Label> action = (Action<string, string, ProgressBar, Label>)iar.AsyncState; //得到異步結果 action.EndInvoke(iar); //更新異步操作狀態 lblStatus.Invoke(new Action(delegate { lblStatus.Text = "更新完畢。"; })); //暫停 System.Threading.Thread.Sleep(1000); string fileName = Application.StartupPath + "\\LogAnalysiser.exe"; //異步更新結束,啟動主程序 Process.Start(fileName); //退出異步更新程序 Application.Exit(); }
其次,需要說明的是,既然我們是更新軟件,那么肯定需要一個網絡地址存儲更高版本的文件,這里我專門創建了一個WebService用來處理更新程序所發出的請求。
在這個WebService中,我在web.cong文件中的configurations節點下新添加了一個子節點(這個涉及到在Web.config中進行自定義節點的設置方面的知識,可以參見我的文章:Asp.net配置文件中自定義節點詳解):
<section name="MySection" type="UpgradeServer.MySection,UpgradeServer"/>
然后在CONFIGSECTIONS節點外面加入如下配置的節點:
<MySection> <add version ="1.2.0.0" fileName="http://localhost:2187/DownLoadVersion/LogAnalysiser.exe"></add> </MySection>
其中 version代表版本號,fileName代表待更新的文件的網絡地址。
這樣配置完成之后,在代碼中,我們就可以使用兩個函數暴露出待更新的軟件的版本號和更新地址:
[WebMethod] public string GetUpgradeVersion() { MySection section = (MySection)ConfigurationManager.GetSection("MySection"); MySectionItem item = section.Item; return item.Version; } [WebMethod] public string GetUpgradeFileName() { MySection section = (MySection)ConfigurationManager.GetSection("MySection"); MySectionItem item = section.Item; return item.FileName; }
那么當程序檢測到目前版本號和WEBSERVER暴露出來的版本號一樣的時候,表明服務器上面沒有最新版本,當二者不一致的時候,則證明服務器上面有最新的版本,於是啟動更新組件,進行更新。
代碼如下:
public bool CheckVersionAndUpgrade() { try { if (client == null) { client = new UpgradeFormApplication.UpgradeWebService.Service1SoapClient(); } string upgradeVersion = client.GetUpgradeVersion(); //獲取版本號 string upgradeFileName = client.GetUpgradeFileName(); //獲取更新文件的網絡路徑 string currentVersion = CommonUntil.GetApplicationVersionFromExeFile(); //獲取當前主程序的版本號 if (String.IsNullOrEmpty(upgradeVersion)) { lblStatus.Invoke(new Action(delegate { lblStatus.Text = "當前沒有最新版本。"; })); btnUpgrade.Invoke(new Action(delegate { btnUpgrade.Enabled = false; })); return false; } if (String.IsNullOrEmpty(currentVersion)) { return false; } if (currentVersion.Equals(upgradeVersion)) //如果沒有更高的版本號 { lblStatus.Invoke(new Action(delegate { lblStatus.Text = "當前沒有最新版本。"; })); btnUpgrade.Invoke(new Action(delegate { btnUpgrade.Enabled = false; })); return false; } lblStatus.Invoke(new Action(delegate { lblStatus.Text = "當前存在最新版本" + upgradeVersion + ",點擊更新。。。"; })); return true; } catch { lblStatus.Invoke(new Action(delegate{lblStatus.Text = "不能連接遠程主機獲取更新,請檢查網絡連接!";})); btnUpgrade.Invoke(new Action(delegate { btnUpgrade.Enabled = false; })); return false; } }
那么一旦我們有新的版本需要更新的時候,我們只需要在把這個新的版本放到IIS的形如 http://*******/DownLoadVersion/的路徑下,並且修改web.config文件中的version的值為新版本號即可。當軟件更新組件運行的時候,一旦發現version值改變,就會立即啟動更新程序進行更新。
技術之四:幫助文檔自動生成。
其實這個並不能稱為技術,應為我們應用的是自動生成軟件,但是這個幫助文檔也確實是必不可少的,它可以讓開發人員對軟件的功能一目了然。 說到自動文檔生成,這里我推薦使用.NET文檔生成工具ADB,作者博客為:HTTP://WWW.CNBLOGS.COM/LUCC/ARCHIVE/2008/09/01/1281085.HTML
這個軟件支持多種注釋的智能識別模式,並且支持多程序集合並功能。在使用本軟件之前,強烈建議為程序集生成XML文檔,具體做法是在項目上右擊,選擇“生成標簽”,然后勾選上”XML文檔文件”選項。
當我用ADB加載我的LOGANALYSISER.EXE文件的時候,我們可以看到軟件界面列出了如下的各種公共方法,公共屬性等等。 當我們最后點擊創建文檔按鈕的時候,就得到了一個看上去非常專業的幫助文檔:
好了,這個軟件的介紹就到了這里,如果覺得有幫助,還請幫助頂一下,謝謝。
源碼下載