** 使用 Exception Management Application Block **
作者:郑佐 2004-8-31
在平常的开发过程中处理程序异常必不可少,如何获取更多的异常信息以及对异常信息进行统一处理那是一件值得思考的事情。在 .NET 程序的开发上,微软为我们提供了一个 ( 我认为 ) 比较好的异常处理模块( Exception Management Application Block ),它可以在非特定的 .NET 应用程序中使用。程序模块和文档说明可以到微软网站上 下载 , msdn 文档也有比较详细的说明。
使用这个模块已经好多次了,看了它里面的代码,觉得写得很不错,有些设计很值得参考和拿来使用。故写了一些体会同大家交流和分享。
** 一 ** ** ** ** 、组件的布局 ** ** **
整个组件布局如下:
( 1 )
** 二、主要代码分析 ** ** **
我认为里面最关键的是两个类: ExceptionManagerSectionHandler 和 ExceptionManager 类。
( 1 ) ExceptionManagerSectionHandler 类
它的功能就是负责读取程序配置文件中的自定义配置节的信息,从代码中看到实现了 System.Configuration.IConfigurationSectionHandler 接口,
public class ExceptionManagerSectionHandler : IConfigurationSectionHandler
在实现 IConfigurationSectionHandler.Create 方法的时候,把配置文件中的配置信息都读取到 ExceptionManagementSettings 类实例中进行封装。
在了解方法内部的处理之前,先来看一个标准的配置文件的设置信息:
< configuration >
< configSections >
< section name ="exceptionManagement" type ="Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManagerSectionHandler,
Microsoft.ApplicationBlocks.ExceptionManagement" />
configSections >
< exceptionManagement mode ="on">
< publisher assembly ="ExceptionManagementQuickStartSamples" type ="ExceptionManagementQuickStartSamples.ExceptionPublisher" exclude ="*" include ="ExceptionManagementQuickStartSamples.LogonException, ExceptionManagementQuickStartSamples; ExceptionManagementQuickStartSamples.CustomAppException, ExceptionManagementQuickStartSamples"/>
< publisher assembly ="ExceptionManagementQuickStartSamples" type ="ExceptionManagementQuickStartSamples.ExceptionXMLPublisher" exclude ="*" include ="+Microsoft.ApplicationBlocks.ExceptionManagement.BaseApplicationException, Microsoft.ApplicationBlocks.ExceptionManagement" exceptionFormat ="xml" fileName ="c:\QuickStartSamplesExceptionLog.xml"/>
exceptionManagement >
configuration >
自定义节的名称为 "exceptionManagement" ,在 Type 属性中指名了 ExceptionManagerSectionHandler 类,这样就可以通过 ExceptionManagementSettings config =
(ExceptionManagementSettings)ConfigurationSettings.GetConfig("exceptionManagement"); 来完成封装信息的获取,相当好用。
对 exceptionManagement 元素的分析就在 Create() 方法中完成。
public object Create( object parent, object configContext,XmlNode section)
{
// ……
ExceptionManagementSettings settings = new ExceptionManagementSettings();
// ……
currentAttribute = nodeAttributes.RemoveNamedItem(PUBLISHER_TYPE);
if (currentAttribute != null )
publisherSettings.TypeName = currentAttribute.Value;
// ……
}
里面使用 XmlNamedNodeMap.RemoveNamedItem(string) 方法返回 XmlNode ,这样不需要处理多余的东西。里面一个比较好的设计就是可以为 publisher 元素增加自定义属性和值。如上面配置中的 fileName="c:\QuickStartSamplesExceptionLog.xml" ,这个很灵活,可以在处理特定的异常时使用特定的配置信息。
下面是附加信息填充到 publisherSettings 的 AddOtherAttributes 中的代码:
for ( int i = 0; i < nodeAttributes.Count; i++)
{ publisherSettings.AddOtherAttributes(nodeAttributes.Item(i).Name,nodeAttributes.Item(i).Value);
}
PublisherSettings 类内部是用 NameValueCollection 对象来保存这些信息。
该类的另外一个方法就是处理 publisher 元素的 exclude 属性和 include 属性。
private TypeFilter LoadTypeFilter( string [] rawFilter){// … }
里面的几种处理情况:
- 表示所有异常,
+ 表示包括该类及其继承类。
其他按正常处理。
备注:上面一点的设计有时候也很用得上,而且很容易理解。
下面是一些配置的辅助:
ExceptionManagementMode 枚举,作为 exceptionManagemen 元素的开关;
PublisherMode 枚举,作为 publisher 元素的开关;
PublisherSettings 映射到 publisher 的整个元素,里面包含了 PublisherMode , PublisherFormat , TypeFilter , NameValueCollection (附加节点)。
( 2 ) ExceptionManager 类
该类使用一个私有构造函数来防止被实例化。
private ExceptionManager()
{
}
在讨论 ExceptionManager 类之前先简单的来看一下 DefaultPublisher 类,它实现了 IExceptionPublisher 接口,
public sealed class DefaultPublisher : IExceptionPublisher
功能就是发布异常信息到系统日志中去,由 IExceptionPublisher.Publish 方法来完成具体工作,下一节我们再具体讨论。
回过头来再看 ExceptionManager 的主要方法,
public static void Publish(Exception exception, NameValueCollection additionalInfo)
在该方法体中会先判断在程序配置文件中是否存在模块配置信息,如果没有就间接得调用 DefaultPublisher.Publish 方法来发布异常信息;
if (ConfigurationSettings.GetConfig(EXCEPTIONMANAGEMENT_CONFIG_SECTION) == null )
{
PublishToDefaultPublisher(exception, additionalInfo);
}
EXCEPTIONMANAGEMENT_CONFIG_SECTION 表示 "exceptionManagement" 字符串;
如果有就获取信息并封装到 ExceptionManagementSettings 实例,
ExceptionManagementSettings config =
(ExceptionManagementSettings)ConfigurationSettings.GetConfig(EXCEPTIONMANAGEMENT_CONFIG_SECTION);
分析 config 里的信息来选择调用下面的几个私有静态方法进行异常信息的发布
1 ) PublishToDefaultPublisher 方法
用默认的方式发布,里面是在调用 DefaultPublisher.Publish 方法,整个方法如下,
private static void PublishToDefaultPublisher(Exception exception, NameValueCollection additionalInfo)
{
DefaultPublisher Publisher = new DefaultPublisher();
Publisher.Publish(exception, additionalInfo, null );
}
2 ) PublishInternalException 方法
用来发布模块内部处理过程中产生的错误,我们从它的方法体中可以看到它把信息写到了系统的 ” 应用程序 (Application) ” 日志中了。值得注意的是该方法加了 internal 修饰符。
internal static void PublishInternalException(Exception exception, NameValueCollection additionalInfo)
{
DefaultPublisher Publisher = new DefaultPublisher("Application", resourceManager.GetString("RES_EXCEPTIONMANAGER_INTERNAL_EXCEPTIONS"));
Publisher.Publish(exception, additionalInfo, null );
}
3 ) PublishToCustomPublisher 方法
用来发布自定义的异常信息,根据 publisher.ExceptionFormat 枚举值来决定创建实现了 IExceptionXmlPublisher 或 IExceptionPublisher 接口的类的实例。
if (publisher.ExceptionFormat == PublisherFormat.Xml)
{
IExceptionXmlPublisher XMLPublisher = (IExceptionXmlPublisher)Activate(publisher.AssemblyName, publisher.TypeName);
XMLPublisher.Publish(SerializeToXml(exception, additionalInfo),publisher.OtherAttributes);
}
else
{
IExceptionPublisher Publisher = (IExceptionPublisher)Activate(publisher.AssemblyName, publisher.TypeName);
Publisher.Publish(exception, additionalInfo, publisher.OtherAttributes);
}
产生实例的方法如下:
private static object Activate( string assembly, string typeName)
{
return AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assembly, typeName);
}
另外一个方法就是把异常信息序列化为 XmlDocument 对象,定义如下:
public static XmlDocument SerializeToXml(Exception exception, NameValueCollection additionalInfo){// … }
方法体中就是把信息添加到 xml 节点或属性中,生成一个 XmlDocument 文档。
** 三、自定义接口 **
接下来就是怎样来由用户实现自定义的异常处理,在图( 1 )中我们可以看到程序为我们提个了两个接口, IExceptionXmlPublisher 和 IExceptionPublisher ,并且都定义了 Publish 方法。
具体如下:
public interface IExceptionPublisher
{
void Publish(Exception exception, NameValueCollection additionalInfo, NameValueCollection configSettings);
}
public interface IExceptionXmlPublisher
{
void Publish(XmlDocument exceptionInfo, NameValueCollection configSettings);
}
没什么东西,就来说明一下上面方法中使用的参数:
Exception 表示异常类,指的就是程序中产生的异常,介意程序中定义的异常类从 BaseApplicationException 类产生,这样可以包含下面信息:
1〉 Environment.MachineName
2〉 Thread.CurrentPrincipal.Identity.Name
3〉 WindowsIdentity.GetCurrent().Name
4〉 AppDomain.CurrentDomain.FriendlyName
additionalInfo ,指异常的附加信息,包含如下,
1〉 Environment.MachineName
2〉 DateTime.Now
3〉 Assembly.GetExecutingAssembly().FullName
4〉 AppDomain.CurrentDomain.FriendlyName
<SPAN lang=EN-US style="FONT-SIZE: 9pt; FONT-FAMILY: 新宋体; mso-