• 错误日志之观察者模式


    星期一

    情景

    早晨,项目组长来到小明身边,“有人反映咱们的项目有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     }
    View Code

     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     }
    View Code

    开工

    整理思路:发生错误时要执行自己需要的代码,只需要继承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             //创建一个线程池,将方法排入队列以便执行
    10             ThreadPool.QueueUserWorkItem(o => {
    11                 while (true)
    12                 {
    13                     lock (ExceptionStringQueue)
    14                     {
    15                         //如果有错误信息写入日志
    16                         if (ExceptionStringQueue.Count > 0)
    17                         {
    18                             string exceptionString = ExceptionStringQueue.Dequeue();
    19                             //把错误信息写到日志文件中
    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         //给外部提供方法,将错误信息写入队列
    37         public static void Write(string exceptionString)
    38         {
    39             lock (ExceptionStringQueue)
    40             {
    41                 //将错误信息添加到队列
    42                 ExceptionStringQueue.Enqueue(exceptionString);
    43             }
    44         }
    45     }
    View Code

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

     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     }
    View Code

    自定义错误测试

    1 throw new Exception("自定义错误");
    View Code

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

    星期二

    情景

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

    分析

    查看昨天写的代码

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

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

    开工

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

    创建IWriteLog接口

    1     public interface IWriteLog
    2     {
    3         //把错误信息写到相应的地方
    4         void WriteLog(string exceptionString);
    5     }
    View Code

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

    1     public class WriteLogToText : IWriteLog
    2     {
    3         public void WriteLog(string exceptionString)
    4         {
    5             //将错误信息写入文本
    6         }
    7     }
    View Code

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

    1     public class WriteLogToSqlServer : IWriteLog
    2     {
    3         public void WriteLog(string exceptionString)
    4         {
    5             //将错误信息写入数据库
    6         }
    7     }
    View Code

    对变化点进行修改

    1     string exceptionString = ExceptionStringQueue.Dequeue();
    2     //依赖接口
    3     IWriteLog writeLog = new WriteLogToSqlServer();
    4     //IWriteLog writeLog = new WriteLogToText();
    5     //把错误信息写到相应的地方
    6     writeLog.WriteLog(exceptionString);

     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                 while (true)
    19                 {
    20                     lock (ExceptionStringQueue)
    21                     {
    22                         //如果有错误信息写入日志
    23                         if (ExceptionStringQueue.Count > 0)
    24                         {
    25                             string exceptionString = ExceptionStringQueue.Dequeue();
    26                             //发布
    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         //给外部提供方法,将错误信息写入队列
    44         public static void Write(string exceptionString) {
    45             lock (ExceptionStringQueue)
    46             {
    47                 //将错误信息添加到队列
    48                 ExceptionStringQueue.Enqueue(exceptionString);
    49             }
    50         }
    51     }

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

    星期四

    采用Log4Net

    总结

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

    什么时候使用?

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

  • 相关阅读:
    SpringMVC学习笔记----常用注解
    python常用模块学习1
    python基础模块,包
    python-验证功能的装饰器示例
    python闭包及装饰器
    关于windows服务器配置
    python高阶函数
    python-生成器和迭代器
    linux--基础知识5
    python基础-文件操作的其他方法
  • 原文地址:https://www.cnblogs.com/MedlarCanFly/p/11480095.html
Copyright © 2020-2023  润新知