欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

错误日志之观察者模式

程序员文章站 2022-06-20 08:31:23
星期一 情景 早晨,项目组长来到小明身边,“有人反映咱们的项目有Bug” “什么Bug?” “不知道,你添加一个日志模块自己看记录去。” ”...“ 分析 在MVC全局过滤器中自己添加有异常过滤器。 Global.asax 1 public class MvcApplication : System ......

星期一

情景

早晨,项目组长来到小明身边,“有人反映咱们的项目有bug” “什么bug?” “不知道,你添加一个日志模块自己看记录去。” ”...“

分析

在mvc全局过滤器中自己添加有异常过滤器。

global.asax

 1     public class mvcapplication : system.web.httpapplication
 2     {
 3         protected void application_start()
 4         {
 5             arearegistration.registerallareas();
 6             //注册全局过滤器
 7             filterconfig.registerglobalfilters(globalfilters.filters);
 8             routeconfig.registerroutes(routetable.routes);
 9             bundleconfig.registerbundles(bundletable.bundles);
10         }
11     }

 

 filterconfig.cs

1     public class filterconfig
2     {
3         public static void registerglobalfilters(globalfiltercollection filters)
4         {
5             //向全局过滤器中添加异常过滤器
6             //只要你的项目出现了异常,就会执行过滤器里的onexception方法
7             filters.add(new handleerrorattribute());
8         }
9     }

 

开工

整理思路:发生错误时要执行自己需要的代码,只需要继承iexceptionfilter,重写onexception方法,然后把自己的过滤器注册到全局即可。

创建过滤器,myexceptionfilter类

 1     //因为微软已经提供了一个handleerrorattribute类(它其实也是继承了iexceptionfilter),所以我们只需继承它即可
 2     public class myexceptionfilter: handleerrorattribute
 3     {
 4         //重写onexception方法
 5         public override void onexception(exceptioncontext filtercontext)
 6         {
 7             base.onexception(filtercontext);
 8 
 9             //把错误写到日志文件里面去
10             //思考:如果同时来了多个错误,一起向文件中写内容,就会发生同时访问同一个文件问题。你会怎么解决?
11             //提示:锁、队列
12 
13             //loghelper类用来把错误写到日志里面去
14             loghelper.write(filtercontext.exception.tostring());
15 
16         }
17     }

 

 

loghelper类,用来把错误写到日志里面去

 1     public class loghelper
 2     {
 3         //添加一个静态的异常信息队列,只要出现异常就写到队列中。
 4         public static queue<string> exceptionstringqueue = new queue<string>();
 5 
 6         //第一次用到该类型时会执行静态构造函数,只被执行一次
 7         static loghelper() {
 8             //创建一个线程池,将方法排入队列以便执行
 9             threadpool.queueuserworkitem(o=> {
10                 lock (exceptionstringqueue) {
11                     string exceptionstring = exceptionstringqueue.dequeue();
12                     //把错误信息写到日志文件中
13                     using (system.io.streamwriter file = new system.io.streamwriter(@"c:\log.txt", true))
14                     {
15                         file.writeline(exceptionstring);// 直接追加文件末尾,换行 
16                     }
17                 }
18             });
19         }
20 
21 
22         //给外部提供方法,将错误信息写入队列
23         public static void write(string exceptionstring) {
24             lock (exceptionstringqueue)
25             {
26                 //将错误信息添加到队列
27                 exceptionstringqueue.enqueue(exceptionstring);
28             }
29         }
30     }

 

把自己的过滤器注册到全局

 1     public class filterconfig
 2     {
 3         public static void registerglobalfilters(globalfiltercollection filters)
 4         {
 5             //向全局过滤器中添加异常过滤器
 6             //只要你的项目出现了异常,就会执行过滤器里的onexception方法
 7             //filters.add(new handleerrorattribute());
 8             //把自己的过滤器注册到全局
 9             filters.add(new myexceptionfilter());
10         }
11     }

 

自定义错误测试

1 throw new exception("自定义错误");

 

错误日志之观察者模式

 

ok,大功告成,以后就可以根据日志来找错误了。 

星期二

情景

早晨,项目组长又来到小明身边,”昨天我用了你的错误日志功能,还不错,但是你将日志写在文件中整理不是太方便,还存在共享冲突问题,你改下写到数据库中“ ”...“

分析

查看昨天写的代码

错误日志之观察者模式

 

 发现此处是一个变化点,有可能写到文件中,有可能写到数据库中,有可能......

不就是写到不同的地方么,简单,多态就能搞定了。

开工

依赖于抽象,而不依赖于具体

创建iwritelog接口

1     public interface iwritelog
2     {
3         //把错误信息写到相应的地方
4         void writelog(string exceptionstring);
5     }

创建writelogtotext类实现接口,用来写入文本

1     public class writelogtotext : iwritelog
2     {
3         public void writelog(string exceptionstring)
4         {
5             //将错误信息写入文本
6         }
7     }

 

创建writelogtosqlserver类实现接口,用来写入数据库

1     public class writelogtosqlserver : iwritelog
2     {
3         public void writelog(string exceptionstring)
4         {
5             //将错误信息写入数据库
6         }
7     }

 

对变化点进行修改

 1             threadpool.queueuserworkitem(o=> {
 2                 lock (exceptionstringqueue) {
 3                     string exceptionstring = exceptionstringqueue.dequeue();
 4                     //依赖接口
 5                     iwritelog writelog = new writelogtosqlserver();
 6                     //iwritelog writelog = new writelogtotext();
 7                     //把错误信息写到相应的地方
 8                     writelog.writelog(exceptionstring);
 9                     
10                 }
11             });

 

ok,大功告成,又可以去美滋滋了...

星期三

情景

早晨,项目组长再一次来到小明身边,”经过我的思考,我觉得把错误信息同时写到文本和数据库中比较好“ ”为什么?“ “需求” “...”

分析

错误日志之观察者模式

 

 

 错误信息有可能要写到不同的地方,而且不知道有多少地方,说不定明天又加了一个redis、后天再加一个....

这时候我们可以考虑创建一个集合来保存都需要写到那些地方去。(这里插一句:设计模式只是一种思想,实现方式肯定是不唯一的,但是思想是精髓,不能说这个代码是这个模式,换一种方式实现就不是这个模式了。

然后依次写入即可。

开工

对loghelper进行修改

 1     public class loghelper
 2     {
 3         //添加一个静态的异常信息队列,只要出现异常就写到队列中。
 4         public static queue<string> exceptionstringqueue = new queue<string>();
 5 
 6         //定义一个集合来存放所有的 观察者,
 7         public static ilist<iwritelog> writeloglist = new list<iwritelog>();
 8 
 9         //第一次用到该类型时会执行静态构造函数,只被执行一次
10         static loghelper() {
11 
12             //观察(订阅)
13             writeloglist.add(new writelogtosqlserver());
14             writeloglist.add(new writelogtotext());
15 
16             //创建一个线程池,将方法排入队列以便执行
17             threadpool.queueuserworkitem(o=> {
18                 lock (exceptionstringqueue) {
19                     string exceptionstring = exceptionstringqueue.dequeue();
20 
21                     //发布
22                     foreach (var writelog in writeloglist)
23                     {
24                         writelog.writelog(exceptionstring);
25                     }
26 
27                 }
28             });
29         }
30 
31 
32         //给外部提供方法,将错误信息写入队列
33         public static void write(string exceptionstring) {
34             lock (exceptionstringqueue)
35             {
36                 //将错误信息添加到队列
37                 exceptionstringqueue.enqueue(exceptionstring);
38             }
39         }
40     }

 

 

后期如果还需要写入其它地方或者去掉一个的话,只需要add一个或者删除一行即可。当然,现在的代码还有很多可优化的地方,比如把通知者(loghelper)进行抽象,还可以通过配置文件加反射再次解耦。这里就不做过多介绍了。因为已经星期四了...

星期四

采用log4net

总结

观察者模式又叫发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者同时监听同一个对象。当对象状态发生改变时,通知所订阅的观察者。

什么时候使用?

当一个对象改变同时需要改变其他对象,并且还不知道要改变多少对象。这时应该考虑观察者模式。

 

上一篇: Reactor模式

下一篇: C语言之桶排序