星期一
情景
早晨,項(xiàng)目組長(zhǎng)來到小明身邊,“有人反映咱們的項(xiàng)目有Bug” “什么Bug?” “不知道,你添加一個(gè)日志模塊自己看記錄去?!?”...“
分析
在MVC全局過濾器中自己添加有異常過濾器。
Global.asax
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 protected
void Application_Start() 4 { 5 AreaRegistration.RegisterAllAreas(); 6 //
注冊(cè)全局過濾器 7 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 8
RouteConfig.RegisterRoutes(RouteTable.Routes); 9
BundleConfig.RegisterBundles(BundleTable.Bundles);10 } 11 } View Code
?
?FilterConfig.cs
1 public class FilterConfig 2 { 3 public static void
RegisterGlobalFilters(GlobalFilterCollection filters)4 { 5 //向全局過濾器中添加異常過濾器 6
//只要你的項(xiàng)目出現(xiàn)了異常,就會(huì)執(zhí)行過濾器里的OnException方法 7 filters.Add(new HandleErrorAttribute());
8 } 9 } View Code
?
開工
整理思路:發(fā)生錯(cuò)誤時(shí)要執(zhí)行自己需要的代碼,只需要繼承IExceptionFilter,重寫OnException方法,然后把自己的過濾器注冊(cè)到全局即可。
創(chuàng)建過濾器,MyExceptionFilter類
1 //因?yàn)槲④浺呀?jīng)提供了一個(gè)HandleErrorAttribute類(它其實(shí)也是繼承了IExceptionFilter),所以我們只需繼承它即可 2
public class MyExceptionFilter: HandleErrorAttribute 3 { 4 //重寫OnException方法
5 public override void OnException(ExceptionContext filterContext) 6 { 7
base.OnException(filterContext); 8 9 //把錯(cuò)誤寫到日志文件里面去 10 //
思考:如果同時(shí)來了多個(gè)錯(cuò)誤,一起向文件中寫內(nèi)容,就會(huì)發(fā)生同時(shí)訪問同一個(gè)文件問題。你會(huì)怎么解決?11 //提示:鎖、隊(duì)列 12 13 //
LogHelper類用來把錯(cuò)誤寫到日志里面去 14 LogHelper.Write(filterContext.Exception.ToString());
15 16 } 17 }
?
?
LogHelper類,用來把錯(cuò)誤寫到日志里面去
1 public class LogHelper 2 { 3 //添加一個(gè)靜態(tài)的異常信息隊(duì)列,只要出現(xiàn)異常就寫到隊(duì)列中。 4 public
static Queue<string> ExceptionStringQueue = new Queue<string>(); 5 6 //
第一次用到該類型時(shí)會(huì)執(zhí)行靜態(tài)構(gòu)造函數(shù),只被執(zhí)行一次 7 static LogHelper() 8 { 9 //創(chuàng)建一個(gè)線程池,將方法排入隊(duì)列以便執(zhí)行
10 ThreadPool.QueueUserWorkItem(o => { 11 while (true) 12 { 13 lock
(ExceptionStringQueue)14 { 15 //如果有錯(cuò)誤信息寫入日志 16 if (ExceptionStringQueue.Count >
0) 17 { 18 string exceptionString = ExceptionStringQueue.Dequeue(); 19 //
把錯(cuò)誤信息寫到日志文件中 20 using (System.IO.StreamWriter file = new System.IO.StreamWriter(
@"C:\Log.txt", true)) 21 { 22 file.WriteLine(exceptionString);// 直接追加文件末尾,換行 23
}24 } 25 else 26 { 27 Thread.Sleep(1000); 28 } 29 30 } 31 } 32 }); 33 }
34 35 36 //給外部提供方法,將錯(cuò)誤信息寫入隊(duì)列 37 public static void Write(string exceptionString)
38 { 39 lock (ExceptionStringQueue) 40 { 41 //將錯(cuò)誤信息添加到隊(duì)列 42
ExceptionStringQueue.Enqueue(exceptionString);43 } 44 } 45 } View Code
?
把自己的過濾器注冊(cè)到全局
1 public class FilterConfig 2 { 3 public static void
RegisterGlobalFilters(GlobalFilterCollection filters) 4 { 5 //向全局過濾器中添加異常過濾器
6 //只要你的項(xiàng)目出現(xiàn)了異常,就會(huì)執(zhí)行過濾器里的OnException方法 7 //filters.Add(new
HandleErrorAttribute()); 8 //把自己的過濾器注冊(cè)到全局 9 filters.Add(new
MyExceptionFilter());10 } 11 } View Code
?
自定義錯(cuò)誤測(cè)試
1 throw new Exception("自定義錯(cuò)誤"); View Code
?
?OK,大功告成,以后就可以根據(jù)日志來找錯(cuò)誤了。?
星期二
情景
早晨,項(xiàng)目組長(zhǎng)又來到小明身邊,”昨天我用了你的錯(cuò)誤日志功能,還不錯(cuò),但是你將日志寫在文件中整理不是太方便,還存在共享沖突問題,你改下寫到數(shù)據(jù)庫(kù)中“ ”...“
分析
查看昨天寫的代碼
發(fā)現(xiàn)此處是一個(gè)變化點(diǎn),有可能寫到文件中,有可能寫到數(shù)據(jù)庫(kù)中,有可能......
不就是寫到不同的地方么,簡(jiǎn)單,多態(tài)就能搞定了。
開工
依賴于抽象,而不依賴于具體
創(chuàng)建IWriteLog接口
1 public interface IWriteLog 2 { 3 //把錯(cuò)誤信息寫到相應(yīng)的地方 4 void WriteLog(string
exceptionString);5 } View Code
創(chuàng)建WriteLogToText類實(shí)現(xiàn)接口,用來寫入文本
1 public class WriteLogToText : IWriteLog 2 { 3 public void WriteLog(string
exceptionString)4 { 5 //將錯(cuò)誤信息寫入文本 6 } 7 } View Code
?創(chuàng)建WriteLogToSqlServer類實(shí)現(xiàn)接口,用來寫入數(shù)據(jù)庫(kù)
1 public class WriteLogToSqlServer : IWriteLog 2 { 3 public void WriteLog(
string exceptionString) 4 { 5 //將錯(cuò)誤信息寫入數(shù)據(jù)庫(kù) 6 } 7 } View Code
?
對(duì)變化點(diǎn)進(jìn)行修改
1 string exceptionString = ExceptionStringQueue.Dequeue(); 2 //依賴接口 3
IWriteLog writeLog =new WriteLogToSqlServer(); 4 //IWriteLog writeLog = new
WriteLogToText();5 //把錯(cuò)誤信息寫到相應(yīng)的地方 6 writeLog.WriteLog(exceptionString);
?OK,大功告成,又可以去美滋滋了...
星期三
情景
早晨,項(xiàng)目組長(zhǎng)再一次來到小明身邊,”經(jīng)過我的思考,我覺得把錯(cuò)誤信息同時(shí)寫到文本和數(shù)據(jù)庫(kù)中比較好“ ”為什么?“ “需求” “...”
分析
錯(cuò)誤信息有可能要寫到不同的地方,而且不知道有多少地方,說不定明天又加了一個(gè)Redis、后天再加一個(gè)....
這時(shí)候我們可以考慮創(chuàng)建一個(gè)集合來保存都需要寫到那些地方去。(這里插一句:
設(shè)計(jì)模式只是一種思想,實(shí)現(xiàn)方式肯定是不唯一的,但是思想是精髓,不能說這個(gè)代碼是這個(gè)模式,換一種方式實(shí)現(xiàn)就不是這個(gè)模式了。)
然后依次寫入即可。
開工
對(duì)LogHelper進(jìn)行修改
1 public class LogHelper 2 { 3 //添加一個(gè)靜態(tài)的異常信息隊(duì)列,只要出現(xiàn)異常就寫到隊(duì)列中。 4 public
static Queue<string> ExceptionStringQueue = new Queue<string>(); 5 6 //
定義一個(gè)集合來存放所有的 觀察者, 7 public static IList<IWriteLog> writeLogList = new
List<IWriteLog>(); 8 9 //第一次用到該類型時(shí)會(huì)執(zhí)行靜態(tài)構(gòu)造函數(shù),只被執(zhí)行一次 10 static LogHelper() { 11
12 //觀察(訂閱) 13 writeLogList.Add(new WriteLogToSqlServer()); 14 writeLogList.Add(
new WriteLogToText()); 15 16 //創(chuàng)建一個(gè)線程池,將方法排入隊(duì)列以便執(zhí)行 17
ThreadPool.QueueUserWorkItem(o=> { 18 while (true) 19 { 20 lock
(ExceptionStringQueue)21 { 22 //如果有錯(cuò)誤信息寫入日志 23 if (ExceptionStringQueue.Count >
0) 24 { 25 string exceptionString = ExceptionStringQueue.Dequeue(); 26 //發(fā)布 27
foreach (var writeLog in writeLogList) 28 { 29
writeLog.WriteLog(exceptionString);30 } 31 } 32 else 33 { 34 Thread.Sleep(
1000); 35 } 36 } 37 } 38 39 }); 40 } 41 42 43 //給外部提供方法,將錯(cuò)誤信息寫入隊(duì)列 44 public
static void Write(string exceptionString) { 45 lock (ExceptionStringQueue) 46 {
47 //將錯(cuò)誤信息添加到隊(duì)列 48 ExceptionStringQueue.Enqueue(exceptionString); 49 } 50 }
51 }
后期如果還需要寫入其它地方或者去掉一個(gè)的話,只需要Add一個(gè)或者刪除一行即可。當(dāng)然,現(xiàn)在的代碼還有很多可優(yōu)化的地方,比如把通知者
(LogHelper)進(jìn)行抽象,還可以通過配置文件加反射再次解耦。這里就不做過多介紹了。因?yàn)橐呀?jīng)星期四了...
星期四
采用Log4Net
總結(jié)
觀察者模式又叫發(fā)布-訂閱模式,定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者同時(shí)監(jiān)聽同一個(gè)對(duì)象。當(dāng)對(duì)象狀態(tài)發(fā)生改變時(shí),通知所訂閱的觀察者。
什么時(shí)候使用?
當(dāng)一個(gè)對(duì)象改變同時(shí)需要改變其他對(duì)象,并且還不知道要改變多少對(duì)象。這時(shí)應(yīng)該考慮觀察者模式。
?
熱門工具 換一換