代码改变世界

一个自己写的组件--异常报告(3):开发一个异常处理程序

2008-07-04 22:37  GUO Xingwang  阅读(1929)  评论(6编辑  收藏  举报
    
    好像好多开发者都在使用一个叫做
log4net的组件,开始我对它不怎么了解,后来查了查发现我的组件和那个组件还是有很多不同的地方,例如使用时,我的输出信息是异常的上下文(包括异常发生时的参数信息),而log4net是一个日志组件,输出信息是由开发者自己输入的信息,此外个人觉得log4net组件配置比较复杂,当然这都是我个人的观点了,哈哈,谁不说自己的东西好啊!

    上文中我对异常报告组件进行了重构和配置,实际上在上文中MyDebuger组件就已经完成了。我在设计这个组件时考虑到了它的扩展性,其中一个重要的扩展就是可以定义新的异常处理程序模块之后通过配置与主程序一起工作。这节我就开发一个简单的异常处理程序,之后进行配置实现写日志到文件系统。

    主程序自带了一个Windows日志异常处理程序,也是默认使用的,它可以提供给Web程序和Windows程序使用。而这节的这个异常处理程序主要是为Windows程序打造的,就是将异常信息输出到一个或多个日志目录中以.log文件保存,它可以使用两种模式来记录日志。在此解释一下为什么说专门为Winodws程序打造的呢,实际上Asp.net也可以使用,但是由于Asp.net的工作者进程在默认情况下没有对文件系统的写的权限,需要额外的设置才可以使用,尤其在部署时很不方便(而我的这个组件的设计目标是为了在部署以后发现异常使用的)。

    现在让我们看看这个异常处理模块是如何实现的吧!
    首先我们需要对于OutputCreatorIOutput进行实现.

    FileSystemLogsOutputCreator.cs

 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using MyDebuger;
 5
 6namespace FileSystemLogsOutput
 7{
 8    class FileSystemLogsOutputCreator : OutputCreator
 9    {
10        public override IOutput FactoryMethod()
11        {
12            return new FileSystemLogsOutput();
13        }

14    }

15}

16

    这里对上篇文章提到的工厂进行了实现。

        FileSystemLogsOutput.cs:
  1using System;
  2using System.Collections.Generic;
  3using System.Text;
  4using System.Globalization;//使用DateTimeFormatInfo
  5using System.Configuration;
  6using System.IO;
  7using MyDebuger;
  8
  9namespace FileSystemLogsOutput
 10{
 11    /// <summary>
 12    /// 将异常信息写入到文件系统
 13    /// 实现了IOutput接口的处理程序
 14    /// </summary>

 15    public class FileSystemLogsOutput : IOutput
 16    {
 17        public bool OutputLog(string methodName,
 18                                                string parametersList,
 19                                                Exception exception,
 20                                                string attachedMessage)
 21        {
 22            try
 23            {
 24                //获得配置节点
 25                FileSystemLogsOutputSettingsSection config =
 26                    (FileSystemLogsOutputSettingsSection)ConfigurationManager.GetSection("fileSystemLogsOutputSettings");
 27
 28                //通过配置进行指定日志路径
 29                OutputFoldersCollection __outputFoldersCollection = config.OutputFolders;
 30
 31                if (__outputFoldersCollection != null)
 32                {
 33                    foreach (OutputFoldersElement __outputFoldersElement in __outputFoldersCollection)
 34                    {
 35                        string __path = __outputFoldersElement.Path;
 36
 37                        WriteLogFile(GetFilePath(__path, config._LogFileSavePattern),
 38                            methodName, parametersList, exception, attachedMessage);
 39                    }

 40                }

 41
 42                return true;
 43            }

 44            catch (Exception ex)
 45            {
 46                string message;
 47                message = ex.Message;
 48                return false;
 49            }

 50        }

 51
 52        //写日志到文件
 53        private void WriteLogFile(string strPathName,
 54                                                string methodName,
 55                                                string parametersList,
 56                                                Exception exception,
 57                                                string attachedMessage)
 58        {
 59            StreamWriter __streamWriter;
 60
 61            //无论文件是否存在都以追加方式写入
 62            __streamWriter = new StreamWriter(strPathName, true, Encoding.Unicode);
 63
 64            try
 65            {
 66                //自动刷新缓冲,格式输出
 67                __streamWriter.AutoFlush = true;
 68                __streamWriter.WriteLine("\n\n========================================begin========================================\n");
 69                __streamWriter.WriteLine(DateTime.Now.ToString("f", DateTimeFormatInfo.InvariantInfo) + "\n");
 70                __streamWriter.WriteLine("Method:" + methodName + "\n");
 71                __streamWriter.WriteLine("Parameters:" + parametersList + "\n");
 72                __streamWriter.WriteLine("Message:" + exception.Message + "\n");
 73                __streamWriter.WriteLine("Trace:" + exception.StackTrace + "\n");
 74                __streamWriter.WriteLine("Attached Message:" + attachedMessage + "\n");
 75                __streamWriter.WriteLine("==========================================end==========================================\n\n");
 76
 77            }

 78            catch (Exception ex)
 79            {
 80                string message;
 81                message = ex.Message;
 82                throw ex;
 83            }

 84            finally
 85            {
 86                __streamWriter.Close();
 87                __streamWriter.Dispose();
 88                __streamWriter = null;
 89            }

 90        }

 91
 92        //获得文件路径
 93        private string GetFilePath(string folder, LogFileSavePattern __logFileSavePattern)
 94        {
 95            //获得文件全名
 96            string __fileName = "";
 97            try
 98            {
 99                //根据文件保存模式获得文件名
100                if (__logFileSavePattern == LogFileSavePattern.varyByContextWraper)
101                    __fileName = folder + "/" +
102                        DateTime.Today.ToString("yy-MM-dd", DateTimeFormatInfo.InvariantInfo) + "__" +
103                        Path.GetRandomFileName() + ".log";
104                else
105                    __fileName = folder + "/" +
106                       DateTime.Today.ToString("yy-MM-dd", DateTimeFormatInfo.InvariantInfo) + ".log";
107            }

108            catch (Exception ex)
109            {
110                string message;
111                message = ex.Message;
112                throw ex;
113            }

114
115            return __fileName;
116        }

117    }

118}

119

    这里对上篇文章提到的IOutput接口进行了实现。这个类一共有三个方法,第一个是实现了IOutput接口的方法,这个不用说了,第二个是个私有的方法,是实现写日志的具体操作,写入日志到第一个参数指定的文件中,第三个也是一个私有方法,主要是根据不同的日志保存模式获得不同的文件全名,日志的保存模式一个有两种:

1public enum LogFileSavePattern
2    {
3        varyByDate,//一天一个日志文件
4        varyByContextWraper//一个异常一个日志文件
5}

6
 

这个保存模式在配置中实现。对于这个处理程序的配置形式如下:


1<fileSystemLogsOutputSettings logFileSavePattern="varyByDate">
2    <outputFolders>
3      <!--<add name="path1" path="C:/logs" />-->
4    </outputFolders>
5  </fileSystemLogsOutputSettings>
6</configuration>

    很简单吧,最重要的就是logFileSavePattern可以设置不同的存储模式,outputFolders是所有输出的目录,支持同时多个,目录中我使用了”/”主要是编程上的方便,哈哈,见笑了。

    那么我们怎样使用这个异常处理程序呢?
首先将FileSystemLogsOutput.dll拷贝到应用程序的Bin目录中(和MyDubger.dll放在同一个目录下),之后应用程序的配置文件如下:

 1<?xml version="1.0" encoding="utf-8" ?>
 2<configuration>
 3  <configSections>
 4    <section name="myDebugerSettings" type="MyDebuger.MyDebugerSettingsSection, MyDebuger" />
 5    <section name="fileSystemLogsOutputSettings" type="FileSystemLogsOutput.FileSystemLogsOutputSettingsSection, FileSystemLogsOutput" />
 6  </configSections>
 7  <myDebugerSettings defaultOutputApply="true">
 8    <outputCreators>
 9      <add name="eventLogsOutput" outputCreator="MyDebuger,EventLogsOutputCreator,MyDebuger" />
10    </outputCreators>
11  </myDebugerSettings>
12  <fileSystemLogsOutputSettings logFileSavePattern="varyByDate">
13    <outputFolders>
14      <add name="path1" path="C:/logs" />
15    </outputFolders>
16  </fileSystemLogsOutputSettings>
17</configuration>

    这样就可以通过MyDebuger组件和这个异常处理程序将异常日志输出到C:/logs目录下了,简单吧!

    上一节已经说过了,如果需求有变我们可以开发更多的异常处理程序,例如输出日志到数据库(Web开发时经常使用)。输出日志到邮箱,输出日志到WS,甚至输出到.Net Remoting,到网络上等。

    至此,这个组件已经全部完成了,这个系列也Over了!谢谢大家!

下载输出日志到文件系统异常处理程序模块:
FileSystemLogsOutput_SRC20080704.RAR