微软企业库5.0 学习之路——第三步、为项目加上异常处理(采用自定义扩展方式记录到数据库中)

我们搭建好了项目的整体多数据库环境,实现了项目的多数据库访问,而整个项目中最主要的异常处理却没有进行部署,今天我们就使用企业库中的Exception Handling+Logging模块为项目加上异常处理以及异常日志记录。

(注:关于Exception Handling和Logging模块的相关基本概念可以查看TerryLee的异常处理日志检测这2篇文章)

     首先说一下企业库Logging模块的个人感觉,个人感觉企业库的日志记录太繁琐了,而且要自定义也比较烦,无法通过简单的配置达到我自己的要求,企业库中的日志记录模块在可以记录许多信息如下:

Timestamp: 2010-6-12 3:16:39

Message: HandlingInstanceID: 669fed01-a758-434b-896e-a8e25ebf8c9b
An exception of type 'System.Exception' occurred and was caught.
----------------------------------------------------------------
06/12/2010 11:16:39
Type : System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message : Test
Source : EntLibStudy.Helper
Help link :
Data : System.Collections.ListDictionaryInternal
TargetSite : System.String Test()
Stack Trace :    在 EntLibStudy.Helper.BasePage.Test() 位置 F:\EntLibStudy\Helper\BasePage.cs:行号 87
   在 EntLibStudy.Helper.BasePage.<Page_Load>b__0() 位置 F:\EntLibStudy\Helper\BasePage.cs:行号 81
   在 Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionManagerImpl.Process[TResult](Func`1 action, TResult defaultResult, String policyName) 位置

以下省略N行。。。。。。

这些信息很多都不是我想要的,我想要的仅仅是异常的提示信息,异常发生的时间,以及异常发生的位置,好方便我们第一时间到异常发生的源头进行调试检查(可能企业库的这些异常信息更加有用,但是我个人认为很多时候都会干扰我们),所以我们仅仅需要其中的几条有用的信息就够了,比如Message,Timestamp、Stack Trace和Severity这4个就基本上够用了,所以我做了个处理,就是使用企业库中Logging模块提供的自定义CustomerTraceListener来实现我们需要的功能。

首先建立一个异常日志记录表(SQLite版)

1 CREATE TABLE [ExceptionLog] (
2     [Id] integer PRIMARY KEY AUTOINCREMENT NOT NULL,
3     [Message] nvarchar(1024) NOT NULL,
4     [LogDate] nvarchar(1024) NOT NULL,
5     [ExceptionLevel] nvarchar(32) NOT NULL,
6     [Exception] ntext NOT NULL
7 )

我编写了一个类继承自CustomTraceListener,并重写了记录方法,具体代码如下:

001 using System;
002 using System.Collections.Generic;
003 using System.Data;
004 using System.Data.Common;
005 using System.Diagnostics;
006 using System.Globalization;
007 using System.Linq;
008 using System.Text;
009   
010 using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
011 using Microsoft.Practices.EnterpriseLibrary.Data;
012 using Microsoft.Practices.EnterpriseLibrary.Logging;
013 using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
014 using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
015 using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
016   
017 namespace EntLibStudy.Helper.EntLibExtension.ExceptionExtension
018 {
019     [ConfigurationElementType(typeof(CustomTraceListenerData))]
020     public class ExceptionCustomerListener : CustomTraceListener
021     {
022         string writeLogSQL = String.Empty;
023         Database database;
024         Exception ex;
025   
026         public ExceptionCustomerListener()
027             : base()
028         {
029             database = DBHelper.CreateDataBase();
030         }
031   
032         public override void TraceData(TraceEventCache eventCache, string source,
033             TraceEventType eventType, int id, object data)
034         {
035             if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
036             {
037                 if (data is LogEntry)
038                 {
039                     LogEntry logEntry = data as LogEntry;
040                     ExecuteSQL(logEntry);
041                 }
042                 else if (data is string)
043                 {
044                     Write(data as string);
045                 }
046                 else
047                 {
048                     base.TraceData(eventCache, source, eventType, id, data);
049                 }
050             }
051         }
052   
053         public override void Write(string message)
054         {
055             ExecuteWriteLogSQL(TraceEventType.Information, DateTime.Now, message, database);
056         }
057   
058         public override void WriteLine(string message)
059         {
060             Write(message);
061         }
062   
063         /// <summary>
064         ///执行SQL
065         /// </summary>
066         /// <param name="logEntry">日志对象</param>
067         private void ExecuteSQL(LogEntry logEntry)
068         {
069             using (DbConnection connection = database.CreateConnection())
070             {
071                 try
072                 {
073                     connection.Open();
074                     using (DbTransaction transaction = connection.BeginTransaction())
075                     {
076                         try
077                         {
078                             ExecuteWriteLogSQL(logEntry, database, transaction);
079                             transaction.Commit();
080                         }
081                         catch
082                         {
083                             transaction.Rollback();
084                             throw;
085                         }
086                     }
087                 }
088                 finally
089                 {
090                     connection.Close();
091                 }
092             }
093         }
094   
095         /// <summary>
096         /// 执行写入日志数据库语句
097         /// </summary>
098         /// <param name="severity">异常等级</param>
099         /// <param name="message">消息</param>
100         /// <param name="db">保存日志的数据库实例</param>
101         private void ExecuteWriteLogSQL(TraceEventType severity, DateTime timeStamp, string message, Database db)
102         {
103             writeLogSQL = (string)this.Attributes["writeLogSQL"];
104             DbCommand cmd = db.GetSqlStringCommand(writeLogSQL);
105             string exceptionMessage = Utils.GetBetweenString(message, "Message :", "Source :", 9);
106             string exceptionInfo = Utils.GetBetweenString(message, "Stack Trace :", "Additional Info:", 13);
107             db.AddInParameter(cmd, "@Message", DbType.String, exceptionMessage);
108             db.AddInParameter(cmd, "@LogDate", DbType.DateTime, timeStamp);
109             db.AddInParameter(cmd, "@Level", DbType.String, message);
110             db.AddInParameter(cmd, "@Exception", DbType.String, exceptionInfo);
111             db.ExecuteNonQuery(cmd);
112         }
113   
114         /// <summary>
115         /// 执行写入日志数据库语句
116         /// </summary>
117         /// <param name="logEntry">日志对象</param>
118         /// <param name="db">保存日志的数据库实例</param>
119         /// <param name="transaction">事务对象</param>
120         private void ExecuteWriteLogSQL(LogEntry logEntry, Database db, DbTransaction transaction)
121         {
122             writeLogSQL = (string)this.Attributes["writeLogSQL"];
123             DbCommand cmd = db.GetSqlStringCommand(writeLogSQL); 
124             string exceptionMessage = Utils.GetBetweenString(logEntry.Message, "Message :", "Source :", 9);
125             string exceptionInfo = Utils.GetBetweenString(logEntry.Message, "Stack Trace :", "Additional Info:", 13);
126             db.AddInParameter(cmd, "@Message", DbType.String, exceptionMessage);
127             db.AddInParameter(cmd, "@LogDate", DbType.DateTime, logEntry.TimeStamp.ToLocalTime());
128             db.AddInParameter(cmd, "@Level", DbType.String, logEntry.LoggedSeverity);
129             db.AddInParameter(cmd, "@Exception", DbType.String, exceptionInfo);
130             db.ExecuteNonQuery(cmd, transaction);
131         }
132     }
133 }

其中在类的初始化的时候获取配置文件的默认数据库对象,通过重写TraceData方法来调用ExecuteSQL方法来执行异常日志插入。

在ExecuteWriteLogSQL方法中有句代码:

 

1 writeLogSQL = (string)this.Attributes["writeLogSQL"];

这个代码就是从配置文件中Listener的Attributes中获取所配置的执行SQL语句(这里不同于Logging模块自带的数据库以存储过程的记录方式,而是使用配置的SQL语句的方式,因为本项目是面向多数据库的,并不是所有的数据库都有存储过程的,比如SQLite),下面看下具体的配置信息:

微软企业库5.0 学习之路——第三步、为项目加上异常处理(采用自定义扩展方式记录到数据库中)_第1张图片

配置文件创建主要分为以下2步:

1、在企业库的配置工具添加一个Exception Handle模块,然后添加一个名为Exception Policy的策略,再为这个策略添加异常类型,默认我选择所有异常类型(All Exceptions),Post Handle Action为: NotifyRethow(对不理解Post Handle Action的处理方式的可以看下下面的解释)

PostHandlingAction 决定了在异常处理链完成后将发生什么活动。默认情况下,PostHandlingAction 被设置为 NotifyRethrow 。
None:应用程序块为此异常执行所有的处理程序,然后在 HandleException 方法的调用点上返回 false 给应用程序。应用程序检查此值以继续运行。
NotifyRethrow:应用程序块为此异常执行所有的处理程序,然后在 HandleException 方法的调用点上返回 true 给应用程序。应用程序检查到此值就重新抛出原始异常。
ThrowNewException:应用程序块为此异常执行所有的处理程序,然后在所有处理程序运行后抛出存在的异常。

2、为异常策略创建处理方式,我这边选择Loggin Exception Handler(在创建的同时配置工具会我们自动创建好Logging模块,并自动创建了一个日志分类:General,不过这个日志分类的默认Listener为event log,就是记录到系统的事件中),这时我们再创建一个CustomerTraceListener选择From File->自定义Listener所在DLL。

这边我碰到了一个问题就是添加了CustomerTraceListener,在对话框中我点击From File选择我编写的自定义Listener所在DLL,可惜没任何反应,不知道是不是要在DLL中做什么处理,所以我只能采用老办法:手写配置文件

首先看下Exception Handle模块的配置信息:

01 <exceptionHandling>
02     <exceptionPolicies>
03       <add name="ExceptionPolicy">
04         <exceptionTypes>
05           <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
06             postHandlingAction="NotifyRethrow">
07             <exceptionHandlers>
08               <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
09                 logCategory="General" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
10                 formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
11                 priority="0" />
12             </exceptionHandlers>
13           </add>
14         </exceptionTypes>
15       </add>
16     </exceptionPolicies>
17   </exceptionHandling>

 

接下来是日志模块配置,在日志模块下我配置了3个Listener,其中Custom Trace Listener为我自定义的异常日志记录,Event Log Listener(系统日志记录)Rolling Flat File Trace Listener(文本文件记录,按天回滚记录)为在日志分类General无法正常记录日志时的记录下日志分类General为何无法记录,因为异常日志默认保存到数据库中,但是如果数据库中存在问题,或者链接被关闭这时就无法正常记录异常,所以:

01 <loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
02     <listeners>
03       <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging"
04         writeLogSQL="insert into ExceptionLog(Message,LogDate,ExceptionLevel,Exception) values(@Message,@LogDate,@Level,@Exception)"
05         type="EntLibStudy.Helper.EntLibExtension.ExceptionExtension.ExceptionCustomerListener, EntLibStudy.Helper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
06         traceOutputOptions="None" name="Custom Trace Listener" initializeData=""
07         formatter="Text Formatter" />
08       <add name="Event Log Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
09         listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
10         source="Enterprise Library Logging" formatter="Text Formatter"
11         log="" machineName="." traceOutputOptions="None" />
12       <add name="Rolling Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
13         listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
14         fileName="rolling.log" formatter="Text Formatter" rollInterval="Day" />
15     </listeners>
16     <formatters>
17       <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
18         template="Timestamp: {timestamp}{newline}
19 Message: {message}{newline}
20 Category: {category}{newline}
21 Priority: {priority}{newline}
22 EventId: {eventid}{newline}
23 Severity: {severity}{newline}
24 Title:{title}{newline}
25 Machine: {localMachine}{newline}
26 App Domain: {localAppDomain}{newline}
27 ProcessId: {localProcessId}{newline}
28 Process Name: {localProcessName}{newline}
29 Thread Name: {threadName}{newline}
30 Win32 ThreadId:{win32ThreadId}{newline}
31 Extended Properties: {dictionary({key} - {value}{newline})}"
32         name="Text Formatter" />
33     </formatters>
34     <categorySources>
35       <add switchValue="All" name="General">
36         <listeners>
37           <add name="Custom Trace Listener" />
38         </listeners>
39       </add>
40     </categorySources>
41     <specialSources>
42       <allEvents switchValue="All" name="All Events" />
43       <notProcessed switchValue="All" name="Unprocessed Category" />
44       <errors switchValue="All" name="Logging Errors & Warnings">
45         <listeners>
46           <add name="Event Log Listener" />
47           <add name="Rolling Flat File Trace Listener" />
48         </listeners>
49       </errors>
50     </specialSources>
51   </loggingConfiguration>

 

在配置完后我们就可以进行代码编写,在页面里进行异常控制。

在ASP.NET中,异常处理主要有4种,执行顺序为:Page_Error事件>ErrorPage属性>Application_Error事件> <customErrors>,我这边采用Page_Error,由于在本项目中我已经建立了BasePage,所有的页面都继承这个页面,所以我只需在这个页面中编写Page_Error事件:

01 protected void Page_Error(object sender, EventArgs e)
02         {
03             //获取最新的异常信息
04             var ex = Server.GetLastError();
05             //处理异常
06             HandleException(ex, "ExceptionPolicy");
07             //清空异常
08             Server.ClearError();
09         }
10         /// <summary>
11         /// 异常处理方法
12         /// </summary>
13         /// <param name="ex">异常信息</param>
14         /// <param name="policy">异常处理策略</param>
15         protected void HandleException(Exception ex, string policy)
16         {
17             bool rethrow = false;
18             var exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();
19             rethrow = exManager.HandleException(ex, policy);
20             if (rethrow)
21             {
22                 this.RedirectPermanent("~/error.aspx");
23             }
24         }

 

其中exManager.HandleException(ex, policy)为根据策略名处理异常,我这边使用的ExceptionPolicy,这个策略的处理方式为异常日志记录,它会帮我们调用到我们自定义的ExceptionCustomerListener 类,进行异常日志记录。

这样我们就完成了统一捕获系统中发生的异常了,本文也到此结束,欢迎大家指点!

你可能感兴趣的:(异常处理)