(注:关于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版)
view source print?
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
|
我编写了一个类继承自CustomTraceListener,并重写了记录方法,具体代码如下:
view source print?
002
|
using System.Collections.Generic;
|
004
|
using System.Data.Common;
|
005
|
using System.Diagnostics;
|
006
|
using System.Globalization;
|
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;
|
017
|
namespace EntLibStudy.Helper.EntLibExtension.ExceptionExtension
|
019
|
[ConfigurationElementType(typeof(CustomTraceListenerData))]
|
020
|
public class ExceptionCustomerListener : CustomTraceListener
|
022
|
string writeLogSQL = String.Empty;
|
026
|
public ExceptionCustomerListener()
|
029
|
database = DBHelper.CreateDataBase();
|
032
|
public override void TraceData(TraceEventCache eventCache, string source,
|
033
|
TraceEventType eventType, int id, object data)
|
035
|
if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
|
037
|
if (data is LogEntry)
|
039
|
LogEntry logEntry = data as LogEntry;
|
040
|
ExecuteSQL(logEntry);
|
042
|
else if (data is string)
|
044
|
Write(data as string);
|
048
|
base.TraceData(eventCache, source, eventType, id, data);
|
053
|
public override void Write(string message)
|
055
|
ExecuteWriteLogSQL(TraceEventType.Information, DateTime.Now, message, database);
|
058
|
public override void WriteLine(string message)
|
066
|
/// <param name="logEntry">日志对象</param>
|
067
|
private void ExecuteSQL(LogEntry logEntry)
|
069
|
using (DbConnection connection = database.CreateConnection())
|
074
|
using (DbTransaction transaction = connection.BeginTransaction())
|
078
|
ExecuteWriteLogSQL(logEntry, database, transaction);
|
079
|
transaction.Commit();
|
083
|
transaction.Rollback();
|
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)
|
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);
|
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)
|
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);
|
其中在类的初始化的时候获取配置文件的默认数据库对象,通过重写TraceData方法来调用ExecuteSQL方法来执行异常日志插入。
在ExecuteWriteLogSQL方法中有句代码:
view source print?
1
|
writeLogSQL = (string)this.Attributes["writeLogSQL"];
|
这个代码就是从配置文件中Listener的Attributes中获取所配置的执行SQL语句(这里不同于Logging模块自带的数据库以存储过程的记录方式,而是使用配置的SQL语句的方式,因为本项目是面向多数据库的,并不是所有的数据库都有存储过程的,比如SQLite),下面看下具体的配置信息:
配置文件创建主要分为以下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模块的配置信息:
view source print?
03
|
<add name="ExceptionPolicy">
|
05
|
<add name="All Exceptions" type="System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
|
06
|
postHandlingAction="NotifyRethrow">
|
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"
|
接下来是日志模块配置,在日志模块下我配置了3个Listener,其中Custom Trace Listener为我自定义的异常日志记录,Event Log Listener(系统日志记录)和Rolling Flat File Trace Listener(文本文件记录,按天回滚记录)为在日志分类General无法正常记录日志时的记录下日志分类General为何无法记录,因为异常日志默认保存到数据库中,但是如果数据库中存在问题,或者链接被关闭这时就无法正常记录异常,所以:
view source print?
01
|
<loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
|
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" />
|
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" />
|
35
|
<add switchValue="All" name="General">
|
37
|
<add name="Custom Trace Listener" />
|
42
|
<allEvents switchValue="All" name="All Events" />
|
43
|
<notProcessed switchValue="All" name="Unprocessed Category" />
|
44
|
<errors switchValue="All" name="Logging Errors & Warnings">
|
46
|
<add name="Event Log Listener" />
|
47
|
<add name="Rolling Flat File Trace Listener" />
|
51
|
</loggingConfiguration>
|
在配置完后我们就可以进行代码编写,在页面里进行异常控制。
在ASP.NET中,异常处理主要有4种,执行顺序为:Page_Error事件>ErrorPage属性>Application_Error事件> <customErrors>,我这边采用Page_Error,由于在本项目中我已经建立了BasePage,所有的页面都继承这个页面,所以我只需在这个页面中编写Page_Error事件:
view source print?
01
|
protected void Page_Error(object sender, EventArgs e)
|
04
|
var ex = Server.GetLastError();
|
06
|
HandleException(ex, "ExceptionPolicy");
|
13
|
/// <param name="ex">异常信息</param>
|
14
|
/// <param name="policy">异常处理策略</param>
|
15
|
protected void HandleException(Exception ex, string policy)
|
18
|
var exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();
|
19
|
rethrow = exManager.HandleException(ex, policy);
|
22
|
this.RedirectPermanent("~/error.aspx");
|
其中exManager.HandleException(ex, policy)为根据策略名处理异常,我这边使用的ExceptionPolicy,这个策略的处理方式为异常日志记录,它会帮我们调用到我们自定义的ExceptionCustomerListener 类,进行异常日志记录。
这样我们就完成了统一捕获系统中发生的异常了,本文也到此结束,欢迎大家指点!