Enterprise Library Step By Step系列(十二):异常处理应用程序块——进阶篇
作者:Terrylee 一.把异常信息Logging到数据库 在日志和监测应用程序块中,有朋友提意见说希望能够把异常信息Logging到数据库中,在这里介绍一下具体的实现方法。 1.创建相关的数据库环境: 我们可以用日志和监测应用程序块自带的SQL语句来创建相关的数据库环境: 创建数据库:
CREATE DATABASE [Logging] ON (NAME = N'Logging', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL\data\Logging.mdf' , SIZE = 1, FILEGROWTH = 10%) LOG ON (NAME = N'Logging_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL\data\Logging_log.LDF' , FILEGROWTH = 10%)
创建表:
CREATE TABLE [dbo].[Log] (
[LogID] [int] IDENTITY (1, 1) NOT NULL , [EventID] [int] NULL , [Category] [nvarchar] (64) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [Priority] [int] NOT NULL , [Severity] [nvarchar] (32) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [Title] [nvarchar] (256) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [Timestamp] [datetime] NOT NULL , [MachineName] [nvarchar] (32) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [AppDomainName] [nvarchar] (2048) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [ProcessID] [nvarchar] (256) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [ProcessName] [nvarchar] (2048) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL , [ThreadName] [nvarchar] (2048) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [Win32ThreadId] [nvarchar] (128) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [Message] [nvarchar] (2048) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [FormattedMessage] [ntext] COLLATE SQL_Latin1_General_CP1_CI_AS NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO 创建存储过程:
1CREATE PROCEDURE WriteLog
2( 3 @EventID int, 4 @Category nvarchar(64), 5 @Priority int, 6 @Severity nvarchar(32), 7 @Title nvarchar(256), 8 @Timestamp datetime, 9 @MachineName nvarchar(32), 10 @AppDomainName nvarchar(2048), 11 @ProcessID nvarchar(256), 12 @ProcessName nvarchar(2048), 13 @ThreadName nvarchar(2048), 14 @Win32ThreadId nvarchar(128), 15 @Message nvarchar(2048), 16 @FormattedMessage ntext 17) 18AS 19 20 INSERT INTO [Log] ( 21 EventID, 22 Category, 23 Priority, 24 Severity, 25 Title, 26 [Timestamp], 27 MachineName, 28 AppDomainName, 29 ProcessID, 30 ProcessName, 31 ThreadName, 32 Win32ThreadId, 33 Message, 34 FormattedMessage 35 ) 36 VALUES ( 37 @EventID, 38 @Category, 39 @Priority, 40 @Severity, 41 @Title, 42 @Timestamp, 43 @MachineName, 44 @AppDomainName, 45 @ProcessID, 46 @ProcessName, 47 @ThreadName, 48 @Win32ThreadId, 49 @Message, 50 @FormattedMessage) 51GO 该SQL语句默认的路径为C:\Program Files\Microsoft Enterprise Library\src\Logging\Sinks\Database\Scripts,直接运行CreateLoggingDatabase.cmd即可。 2.运行配置工具,我们创建一个日志和监测应用程序块,并建一个Database Sink,具体的配置方法在日志和监测应用程序块中讲过了,这里就不重复了,我们看一下它的配置:
注意设置StoredProcName为WriteLog,就是我们刚才创建的存储过程。 3.同时再创建一个Category,起名为DataException,并设置它的Sink为Database Sink。 4.设置Logging Handler的LogCategory为我们刚才创建的DataException,其他的参数暂时默认。
5.至此配置完成,在程序中我们不需要做任何改动(这就是企业库的配置驱动的思想精妙之处^_^)。
1/**//// <summary> 2 /// 日志策略 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 private void btn_Log_Click(object sender, System.EventArgs e) 7 { 8 try 9 { 10 Exception ex = new Exception(); 11 throw ex; 12 } 13 catch(Exception ex) 14 { 15 bool Flag = ExceptionPolicy.HandleException(ex,"Log Policy"); 16 17 if(Flag) 18 { 19 throw; 20 } 21 } 22 } 补充一点:在项目中要添加对Microsoft.Practices.EnterpriseLibrary.Logging.Sinks.Database.dll的引用 二.异常的传播机制 异常的传播机制有以下几种: l 异常自动传播 l 在同一层内部,捕获或者再抛出原有异常 l 捕获,包装和抛出包装后的异常 我们不推荐直接抛出原有异常,因为恶意的用户能够从系统诊断信息中得知应用的详细情况,并从中查找应用的弱点。异常应用程序块提供了一旦配置的Handler执行后,就产生对应的post-handling动作,该动作有如下选项: None - 没有重抛异常的动作。 NotifyRethrow - 告诉调用程序:Policy推荐应该重抛异常。 ThrowNewException - 在所有的Handler执行后,向调用程序抛出最终异常(并不一定是原始的异常)。
可以格式化任何System.Exception类型的异常 能够用来记录或者显示异常的详细信息 字符型格式化器——TextExceptionFormatter:创建在一个屏幕上,日志中或以其他形式表现的,可以表现异常信息的详细记录 XML格式化器——XMLExceptionFormatter:针对一个异常,创建一个用XML表现形式表现记录,每一个异常的属性,均可以被存储为XML元素。 看一下在Enterprise Library Quick Start中提供的自定义的ExceptionFormatter,实现了TextExceptionFormatter类:
1/**//// <summary>
2 /// Summary description for AppTextExceptionFormatter. 3 /// </summary> 4 public class AppTextExceptionFormatter : TextExceptionFormatter 5 { 6 public AppTextExceptionFormatter(TextWriter writer, Exception exception) 7 : base (writer, exception) 8 { 9 } 10 11 protected override void WriteDescription() 12 { 13 // An exception of type {0} occurred and was caught. 14 string line = String.Format("An exception of type {0} occurred and was caught.", base.Exception.GetType().FullName); 15 this.Writer.WriteLine(line); 16 } 17 18 protected override void WriteExceptionType(Type exceptionType) 19 { 20 base.Indent(); 21 base.Writer.WriteLine("Type : {0}", exceptionType.FullName); 22 } 23 24 public override void Format() 25 { 26 //this.Writer.WriteLine("Message : {0}", message); 27 this.WriteDescription(); 28 //this.WriteExceptionType(base.Exception.GetType()); 29 base.WriteMessage(base.Exception.Message); 30 } 31 32 } 四.创建自定义的异常处理器 异常处理应用程序块允许您包装并使用您自己的例外业务处理流程,例如在时间记录系统中填写一个事件,利用业务规范进行包装和替代,利用另外的记录系统进行记录(比较常用的有Log4net,前段时间深渊野鱼介绍的,还没用过^_^),这种灵活的可配置性,将允许您在不同的异常类型及其策略中灵活的配置。
可以通过实现ExceptionHandler抽象类,来创建定制的Handler 1public abstract class ExceptionHandler : ConfigurationProvider, IExceptionHandler
该抽象类继承ConfigurationProvider类,并实现IExceptionHandler接口。ConfigurationProvider抽象类实现了IConfigurationProvider接口,用来读取配置数据。 1public abstract class ConfigurationProvider : IConfigurationProvider
使用支持序列化的数据类型作为配置参数,还有要注意数据类型的简单,避免“Exception Handling Exceptions” 看一下在Enterprise Library Quick Start中提供了定制Handler的实现:
1/**//// <summary>
2 /// Summary description for GlobalPolicyExceptionHandler. 3 /// </summary> 4 public class AppMessageExceptionHandler : ExceptionHandler 5 { 6 public AppMessageExceptionHandler() 7 { 8 } 9 10 public override void Initialize(ConfigurationView configurationView) 11 { 12 } 13 14 public override Exception HandleException(Exception exception, string policyName, Guid correlationID) 15 { 16 DialogResult result = this.ShowThreadExceptionDialog(exception); 17 18 // Exits the program when the user clicks Abort. 19 if (result == DialogResult.Abort) 20 Application.Exit(); 21 22 return exception; 23 } 24 25 // Creates the error message and displays it. 26 private DialogResult ShowThreadExceptionDialog(Exception e) 27 { 28 string errorMsg = e.Message + Environment.NewLine + Environment.NewLine; 29 30 return MessageBox.Show(errorMsg, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Stop); 31 } 32 }
结束语:异常处理应用程序块的进阶篇就写到这里了。 |
|