關(guān)于面向切面編程的部分內(nèi)容-錯(cuò)誤處理機(jī)制
錯(cuò)誤處理機(jī)制。
面對(duì)多個(gè)web服務(wù)器,多線程處理,我們想把錯(cuò)誤信息記錄到一個(gè)txt文檔中。
但是把錯(cuò)誤信息寫(xiě)到內(nèi)存是很快。寫(xiě)到硬盤上就有一堆的問(wèn)題。比如說(shuō)讀寫(xiě)慢、并發(fā)問(wèn)題。
今天我們就利用這個(gè)實(shí)現(xiàn)錯(cuò)誤處理 此文以MVC為例
1、首先要在 golable 文件的 protected void Application_Start()
注冊(cè)一個(gè)錯(cuò)誤處理機(jī)制。
MVC中自帶一個(gè) 過(guò)濾器
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
這里面 我們看到這個(gè)過(guò)濾器
2、其實(shí)就是在 app_Start文件夾下面 的 FilterConfig.cs 文件
3、 打開(kāi)FilterConfig.cs文件
就寫(xiě)了一個(gè)注冊(cè)事件。我們看得到 這個(gè)是對(duì)錯(cuò)誤處理機(jī)制
(當(dāng)然 ,你們看到的是 HandleErrorAttribute 這個(gè)類)
4、所以你們可能覺(jué)得奇怪,我們來(lái)查看MyExceptionAttribut的定義看一下
繼承了HandleErrorAttribute
這里我把這個(gè)類的代碼貼一下
- public class MyExceptionAttribute : HandleErrorAttribute
- {
- // private static object obj = new object();
- public static ConcurrentQueue<Exception> ExceptionQueue = new ConcurrentQueue<Exception>();//定義隊(duì)列
- /// <summary>
- /// 在該方法中捕獲異常。
- /// </summary>
- /// <param name="filterContext"></param>
- public override void OnException(ExceptionContext filterContext)
- {
- base.OnException(filterContext);
- Exception ex = filterContext.Exception;//捕獲異常信息。
- //將異常信息寫(xiě)到隊(duì)列中。
- ExceptionQueue.Enqueue(ex);
- //跳轉(zhuǎn)到錯(cuò)誤頁(yè)面.
- filterContext.HttpContext.Response.Redirect("/Error.html");
- }
- }
主要是定義一個(gè)靜態(tài) 隊(duì)列 ConcurrentQueue
(當(dāng)然你也可以用 Queue。但是微軟說(shuō) 這個(gè)ConcurrentQueue 比 Queue 安全。好像是線程安全的,一堆堆的理論,說(shuō)白了就是用ConcurrentQueue 更安全)
這樣所有的錯(cuò)誤就都在這個(gè)隊(duì)列里面了。(就是內(nèi)存)
這樣總不行吧。內(nèi)存 斷電就沒(méi)有了的啊。
所以我們要想把資料存到 硬盤中。
5、現(xiàn)在又要在
golable 文件的 protected void Application_Start()
中注冊(cè)一個(gè)消費(fèi)線程(這句話后面會(huì)解釋,看不懂就繼續(xù))就是在 protected void Application_Start()中加入這些代碼,***放最前面。
內(nèi)容就是線程池開(kāi)啟一個(gè)線程 從剛剛定義的 MyExceptionAttribute的 ExceptionQueue隊(duì)列里面取出項(xiàng)來(lái)。
將錯(cuò)誤信息最加到文件后面。如果隊(duì)列為空,就線程停留3秒。
- string filePath = Server.MapPath("/Log/");
- ThreadPool.QueueUserWorkItem((a) =>
- {
- while (true)//注意:線程不能結(jié)束。后面寫(xiě)到隊(duì)列中的數(shù)據(jù)沒(méi)法處理。
- {
- // 這里可以加一條 if (MyExceptionAttribute.ExceptionQueue.Count() > 0)
- //{ 發(fā)送郵件到管理員}
- if (MyExceptionAttribute.ExceptionQueue.Count() > 0)
- {
- // Exception ex= MyExceptionAttribute.ExceptionQueue.Dequeue();//從隊(duì)列中取出數(shù)據(jù).
- Exception ex = null;
- bool isResult = MyExceptionAttribute.ExceptionQueue.TryDequeue(out ex);
- if (ex != null && isResult)
- {
- string fullPath = filePath + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
- File.AppendAllText(fullPath, ex.ToString());
- // ILog logger = LogManager.GetLogger("errorMsg");
- // logger.Error(ex.ToString());
- }
- else
- {
- Thread.Sleep(3000);
- }
- }
- else
- {
- Thread.Sleep(3000);//避免造成CPU的空轉(zhuǎn)。
- }
- }
- }, filePath);
6、總結(jié)。
這個(gè)就是一個(gè)生產(chǎn)者消費(fèi)者的模式。
生產(chǎn)者就是 產(chǎn)生錯(cuò)誤的源頭。 消費(fèi)者就是注冊(cè)保存日志的方法。
中間有一個(gè)倉(cāng)庫(kù)就是 那個(gè)靜態(tài)錯(cuò)誤隊(duì)列。
可以看到 系統(tǒng)產(chǎn)生的錯(cuò)誤臨時(shí)存放于內(nèi)存中。然后一個(gè)新的線程 去讀寫(xiě)靜態(tài)錯(cuò)誤隊(duì)列。
正常情況 需要在錯(cuò)誤隊(duì)列里面加一個(gè)錯(cuò)誤隊(duì)列數(shù)字大于1000條的時(shí)候 發(fā)警告到郵箱的功能。那樣感覺(jué)有點(diǎn)問(wèn)題復(fù)雜化了,畢竟這里只是講錯(cuò)誤處理。
7、log4net 我前面講過(guò)的一個(gè)開(kāi)源框架 記錄錯(cuò)誤很好。
這里提供一個(gè)連接 log4net配置方法你可以把那個(gè)一起并到這里。那么就會(huì)有
把protected void Application_Start()中添加 的代碼
改成。注意是改成:
- log4net.Config.XmlConfigurator.Configure();
- //開(kāi)始一個(gè)線程,查看異常隊(duì)列
- string filePath = Server.MapPath("/Log/");
- ThreadPool.QueueUserWorkItem((a) =>
- {
- while (true)//注意:線程不能結(jié)束。后面寫(xiě)到隊(duì)列中的數(shù)據(jù)沒(méi)法處理。
- {
- if (MyExceptionAttribute.ExceptionQueue.Count() > 0)
- {
- // Exception ex= MyExceptionAttribute.ExceptionQueue.Dequeue();//從隊(duì)列中取出數(shù)據(jù).
- Exception ex = null;
- bool isResult = MyExceptionAttribute.ExceptionQueue.TryDequeue(out ex);
- if (ex != null && isResult)
- {
- string fullPath = filePath + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
- //File.AppendAllText(fullPath, ex.ToString());
- ILog logger = LogManager.GetLogger("errorMsg");
- logger.Error(ex.ToString());
- }
- else
- {
- Thread.Sleep(3000);
- }
- }
- else
- {
- Thread.Sleep(3000);//避免造成CPU的空轉(zhuǎn)。
- }
- }
- }, filePath);
這樣就會(huì)按照你的要求把錯(cuò)誤日志記錄到
app_data文件夾下面。(前提是你有未處理的錯(cuò)誤 。呵呵)
看到下圖就成功了
題外話(
沒(méi)有就自己創(chuàng)一個(gè) var s=3/0;
這樣就可以了。
)