通用异常处理框架(转载)

摘要:
通用异常处理框架需要为配置信息和统一的异常处理类和异常日志类管理提供支持,并允许用户以插件的形式扩展自定义异常处理或日志记录方法。整个异常处理过程高度封装了项目调用。无论在项目的哪个位置,只要调用一个唯一的接口,框架就会根据配置信息执行所需的异常处理。用户还可以在ExManagement.interface中实现ILogHandler接口,实现自定义的异常处理类,并通过修改配置文件进行框架调用。
通用异常处理框架

【摘要】

异常捕捉、处理是每个项目中必不可少的一部分,利用反射和XML配置技术实现一个通用的、灵活的、可配置的、高度可自扩展的异常处理框架对项目的整体健壮性以及异常处理效率都是非常重要的。通用异常处理框架中需要提供配置信息的支持以及统一的异常处理类和异常日志记录类管理,并允许用户以插件形式扩展自定义的异常处理或日志记录方式。

一、      问题的提出

由于异常处理在项目中的普遍性,我们会很自然的想到是否可以对异常处理模块进行提取为公用模块,加强项目间的复用,提高项目的开发进度。

并且在异常处理中,因为没有良好的异常处理系统可能造成一些问题:

       若异常处理方式不当,容易造成比较严重的性能问题。

       在项目开发阶段,开发人员需要得到完整的异常错误信息方便分析BUG;在项目发布阶段,用户希望看到的是比较友好的错误信息。一个良好的异常处理系统应能够通过简单的配置方便的达到这样的效果

        异常处理系统应该能够提供足够的信息方便开发人员对BUG异常的准确定位,减少查找BUG产生原因的时间。(该问题在自己的项目中有过深刻教训:因项目中异常信息的不准确,导致一个BUG消耗大量人力时间)

       一个项目中各层对异常处理的方式不同,可能会不同的层编写不同的代码,造成代码使用麻烦以及复用性低,若需修改,可能改动较大。(例如PDM项目中,除UI层外,都是对异常进行包装后抛给上一层捕捉;而UI层需要处理记录异常,并反馈给用户;即便是UI层也应WebUIWinUI采用的不同的方式实现,提供给项目的接口也都有一定区别,增加了编码的复杂程度)

        异常报告信息没有统一管理。很多项目中报告给用户的错误消息,都是程序员在开发中自己编写的,对用户而言,常常不是有效的友好的提示信息。集中管理后可以由BA来进行错误信息的整理,改善用户体验。

        异常记录方式可能会在不同环境有不同的处理,应该能通过配置文件简单的实现,而不必每次为不同的应用编写代码。

        要把异常系统做成一个通用的框架,存在一个比较大的挑战:不同的项目可能因项目的整体构架不同,对异常处理方式会有不同的需求,如何保持通用异常处理框架的灵活性的同时实现复用以及可配置性。

二、解决思路

为了最大程度实现异常处理框架的通用性、可扩展性以及可配置性,采用配置文件结合动态加载插件的方式:框架提供接口,由不同项目根据自己的需要实现接口,完成对异常的处理,以及异常日志的记录;而框架再根据配置信息决定异常处理在不同的情况下的处理策略,并通过调用用户实现的接口来完成异常处理过程。

整个异常处理过程对项目调用高度封装,项目中不论任何地方,只需调用一个唯一的接口,框架就会根据配置信息执行需要的异常处理。

三、实践情况

整体结构

框架主要由ExManagement.ConfigExManagement.InterfaceExManagement.MessageHandler以及ExManagement构成。如图:三-1

 

      ExManagement.Config:用于从配置文件中获取配置信息,并将配置信息转换为一个配置信息实体对象,提供给框架的其他部分使用。

      ExManagement.Interface:提供异常处理类、异常日志记录类以及异常错误报告类的接口和基类,便于用户在框架的基础上进行自定义的扩展

       ExManagement.MessageHandler:对ExManagement.Interface中的IMessage接口实现,提供了WebUIWinUI下的弹出错误信息提示的支持。也可以直接编写一个满足IMessage接口的包,以自己需要的方式弹出错误提示信息。

      ExManagement:是控制和管理框架按照配置信息进行处理的控制类。首先根据异常处理配置加载需要的异常处理类和日志记录类,再按照配置信息内容对异常进行处理。并提供唯一的接口供项目调用。

      ExManagement.Handler:默认的异常处理包,包含了一种对异常处理的具体实现。用户也可以继承ExManagement.Interface中的ExHandlerBase基类,实现自定义的异常处理类,并通过修改配置文件让框架调用。

       ExManagement.LogHandler:默认日志记录包,包含一种将异常日志记录到数据库的具体实现。用户也可以实现ExManagement.Interface中的ILogHandler接口,实现自定义的异常处理类,并通过修改配置文件让框架调用。

 Package.JPG

图三-1

ExManagement.Config

该包主要由两部分组成:实现System.Configuration.IConfigurationSectionHandler接口的ExSectionHandler类和配置信息实体类ExManagerConfig。如图三-2

       ExManagerConfig类中又定义了3个嵌入类:ExHandlerConfigLogHandlerConfigLogHandlerConfigCollection以及三个枚举ExAlertTypeErrorCodeSourceExReturnModeExManagerConfigExHandlerConfigLogHandlerConfig分别对应配置文件中的节点。

ExManagerConfig是一个集合类,从System.Collections.CollectionBase继承,包含若干个ExHandlerConfig对象,并通过索引器访问包含的ExHandlerConfig对象,支持以intstring两种方式索引。
   
ExHandlerConfig中除了包含对应节的属性外,还包含一个LogHandlerConfigCollection对象,是LogHandler类的集合类。
ExManagement.Config.jpg

图三-2



      ExSectionHandler
类从配置文件中按照固定的格式获取到相应的配置信息,并将信息填充到ExManagerConfig


 

配置文件

<configuration>

     <configSections>

         <sectionname="ExManager"type="ExManagement.Config.ExSectionHandler, ExManagement.Config"/>

     configSections>

    

     <ExManagerErrorCodeSource="XML/DB"ConnectionString="" DataTable="">

         <ExHandlerName=""Type=""ReturnMode=""AlertType="WebUI/WinUI/None"/>       

         <ExHandlerName=""Type=""ReturnMode=""AlertType="">

              <LogHandlerType="" ConnectionString="" DataTable=""/>

              <LogHandlerType="" ConnectionString="" DataTable=""/>

         ExHandler>

     ExManager>

configuration>

 

 

首先需要在Web.Config或者App.Config中添加这行

<sectionname="ExManager"type="ExManagement.Config.ExSectionHandler, ExManagement.Config"/>

这句是指定用ExManagement.Config.ExSectionHandler类来处理ExManager配置节点

 

       节点

包含ErrorCodeSourceConnectionString两个属性。

         ErrorCodeSource属性。该属性是设置从何种数据源获取错误编号与错误信息的对应关系。其值只能是枚举ErrorCodeSource的值:XML或者DB

         ConnectionString属性。该属性是设置数据源的位置。若ErrorCodeSource属性设置的是XML,则此处为XML文件的FullName;若ErrorCodeSource属性设置的是DB,则此处为数据库的连接字符串。

         DataTable属性。设置记录异常日志使用的表名。

 

       节点

此节点为节点的子节点,至少得有一个以上的子节点。包含NameTypeReturnMode以及AlertType属性。

         Name属性。为该异常处理器确定一个名称。在项目中调用异常处理方法时,需要指定这个名称。一般建议用层的名字或者项目的名字。

         Type属性。指定异常处理器的类,格式为Type=”ClassName,  AssemblyName”。该类必须从ExHandlerBase继承。

         ReturnMode属性。异常处理器处理后返回给项目的值类型。对应枚举ExReturnMode的值,只能取Exception/ExceptionString/ErrorCode/ErrorString,分别含义是:抛出异常/返回异常详细描述/返回错误编号/返回错误信息。

         AlertType属性。报告异常信息的处理方式。对应枚举ExAlertType的值,只能取None/WebUI/WinUI

 

       < LogHandler>节点

此节点为节点的子节点,节点可以有0到若干个< LogHandler>子节点。

         Type属性,用于指定日志记录处理器的类。格式为Type=”ClassName,  AssemblyName”。该类必须实现IExLogHandler接口。

         ConnectionString属性。该属性是设置记录异常日志的数据源位置。

         DataTable属性。设置记录异常日志使用的表名。

 

ExManagement.Interface

包含了IExLogHandlerI Message两个接口以及ExHandlerBase基类,可以通过实现这些接口来对框架进行扩展。如图三-3
ExManagement.Interface.jpg

图三-3

 

以下是ExHandlerBase基类中加载所有异常日志对象的方法:

publicvoid LoadLogHandler(ExManagerConfig.ExHandlerConfig config)

{

     m_ExHandlerConfig = config;

     foreach (ExManagerConfig.LogHandlerConfig logConfig in config.LogConfigColletion)

     {

         object exLogHandler = System.Activator.CreateInstance(Type.GetType(logConfig.Type));

         lstLogHandler.Add(exLogHandler);

     }

}

通过遍历配置文件中该节包含的所有子节点,并将配置中指定的LogHandler类反射实例化,将对象存入队列中。

ExManagement.MessageHandler

该包包含两个IMessage接口的实现,分别完成对异常信息在WinUIWebUI中的弹出提示功能。WinFormMessageWebMessage分别引用了System.Web.DllSystem. Windows.Forms.Dll。如图三-4
ExManagement.MessageHandler.jpg

 

 

图三-4

 

ExManagement

包含了ExManager类和ErrorInfo类,是框架中核心的业务流程控制模块。如图三-5
ExManagement.jpg

图三-5

 

       ErrorInfo类:根据ErrorId查询对应的ErrorString。在这个类里,会根据在配置文件中节的值去指定的数据源(XML文件或者数据库)查询。

       ExManager类,该类是一个单例类,会在第一次实例化时获取配置信息实体对象,并按照配置文件把所有指定的异常处理类实例化后并存入一个哈希表中。以后实例化该类都会重复使用之前实例化的该对象,避免反射造成的性能影响。通过调用该类的ProcessException()方法将捕捉到的异常对象,错误编号,用来处理异常的异常处理器名(建议为该层的名称)传递给框架,并按照参数从哈希表中取出对应的异常处理对象,调用接口对异常进行处理。

 

 

ExManagement.Handler

只包含一个默认的异常处理器类:DefaultExHandler,它从ExHandlerBase基类继承。下面是该类里最重要的ProcessExeception方法。

 

publicoverridestring ProcessExeception(Exception ex, string strUserId, string strErrorCode)

{

     // 记录异常信息

     LogEx(ex, strUserId, strErrorCode);

 

     switch(m_ExHandlerConfig.ReturnMode)

     {

         // 返回类型为错误编码

         case ExManagement.ExReturnMode.ErrorCode:

          {

              return strErrorCode;

          }

         // 返回类型为详细错误信息(友好提示)

         case ExManagement.ExReturnMode.ErrorString:

          {

              return ExManagement.ExManager.GetErrorInfoByCode(strErrorCode);

          }

         // 返回类型为详细异常信息(Exception.Message

         case ExManagement.ExReturnMode.ExceptionString:

          {

              return ex.Message;

          }

         // 将异常向上抛出

         case ExManagement.ExReturnMode.Exception:

          {

              // 如果是该层自身引发的异常则包装后抛出

              if(ex.GetBaseException() == ex)

              {

                   thrownewException(string.Format("{0}层发生异常:{1}{2}",

                        m_ExHandlerConfig.Name, strErrorCode, ex.Message), ex);

              }

              // 若是捕捉到的是包装后的异常(即上层抛出的)

              else

              {

                   throw ex;

              }

          }

         default:

          {

              returnnull;

          }

     }

}

 

在这个默认的异常处理类的ProcessExeception方法中首先调用该类中处理日志记录的方法,再根据配置中的ExReturnMode决定返回处理的结果方式,对异常进行处理。特别是当设置为返回方式为Exception,即抛出异常对象时是先判断该异常是否是最初发生的异常,还是已经处理包装过的异常,避免重复处理异常(不管异常是来自本层或者其他层)。

 

ExManagement.LogHandler

该包只有一个默认的DefaultLogHandler类,实现了ILogHandler接口,它负责把异常信息记录到数据库中。

我的项目中使用的各层配置文件(示例)

       BusinessLogicLayer

<ExManagerErrorCodeSource="DB"ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataTable="ErrorInfo">

     <ExHandlerName="BusinessLogicLayer"Type="ExManagement.Handler.DefaultExHandler, ExManagement.Handler"ReturnMode="Exception"AlertType="None"/>

         <LogHandlerType="ExManagement.LogHandler.DefaultLogHandler, ExManagement.LogHandler" ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataTable ="ExceptionLog"/>

     ExHandler>

ExManager>

              配置意义为:根据ErrorId到数据库ErrorInfo库中获取ErrorString,定义了一个ExHandler,名为BusinessLogicLayer的框架默认的异常处理类,异常返回方式为抛出异常对象,因为不是UI层,所以AlerTypeNone,该异常处理类用一个默认的LogHandler把异常日志记录到数据库ExceptionLog

 

       BusinessFacadeLayer

<ExManagerErrorCodeSource="DB"ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataBase="ErrorInfo">

     <ExHandlerName="BusinessFacadeLayer"Type="ExManagement.Handler.DefaultExHandler, ExManagement.Handler"ReturnMode="Exception"AlertType="None"/>

         <LogHandlerType="ExManagement.LogHandler.DefaultLogHandler, ExManagement.LogHandler" ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataTable ="ExceptionLog"/>

     ExHandler>

ExManager>

       该层配置除ExHandlerName不同外,与BusinessFacadeLayer的配置基本一致。

 

       WebUILayer

<ExManagerErrorCodeSource="DB"ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataBase="ErrorInfo">

     <ExHandlerName="WebUILayer"Type="ExManagement.Handler.DefaultExHandler, ExManagement.Handler"ReturnMode="ExceptionString"AlertType="WebUI"/>

         <LogHandlerType="ExManagement.LogHandler.DefaultLogHandler, ExManagement.LogHandler" ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataTable ="ExceptionLog"/>

     ExHandler>

ExManager>

       该层的异常处理器的ReturnMode方式为ExceptionString(即Exception.Message),弹出提示方式为WebUI

 

      WinUILayer

<ExManagerErrorCodeSource="DB"ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataBase="ErrorInfo">

     <ExHandlerName="WinUILayer"Type="ExManagement.Handler.DefaultExHandler, ExManagement.Handler"ReturnMode="ExceptionString"AlertType="WinUI"/>

         <LogHandlerType="ExManagement.LogHandler.DefaultLogHandler, ExManagement.LogHandler" ConnectionString="Data Source=PDMDEV151.ITDEV.ZTE.COM.CN;user id=PDM;password=" DataTable ="ExceptionLog"/>

     ExHandler>

ExManager>

       该层和WebUILayer的配置除AlertTypeWinUI方式外,基本一致。

 

以上配置为开发调试方式时,若是在发布到测试或正式环境,只需要把WinUILayerWebUILayer中的ReturnMode属性更改为ErrorString方式,即可让用户看到的是友好的错误信息。

补充:WinUI项目的异常日志记录器可以再增添一个本地异常Log文件方式,当发生异常时,可以根据用户提供的Log文件进行分析。

 

项目中调用

在写好配置文件之后,项目中引用ExManagement包。调用方法如下,项目中任何地方调用处理方式完全一致

// 返回值根据用户配置而不同,可以为ErrorId, ErrorString以及ExceptionString

string strMessage;

try

{

     ;

}

catch(Exception ex)

{

     // 参数ex, 异常对象

     // 参数"BLL001", ErrorId, 即错误编号

     // 参数"BusinessLogicLayer", ExHandlerName, 即异常处理器名称, 建议于层名称对应

     // 参数UserId, 即当前用户Id

     ProcessExeception(ex, "BLL0001", "BusinessLogicLayer", UserId);

}

 

四、效果评价

        可配置性。通过该异常处理框架可以方便的对异常处理进行需要的配置。可配置的内容:

         可以配置多个异常日志记录以不同的方式记录在不同的位置;

         异常处理方式可以有多种:抛出包装后异常对象、返回详细的异常信息(调试用)、返回错误提示信息(发布后给用户看)以及错误编号;

         灵活性。建立在可配置性的基础上,可以组合出多种异常处理方案,以满足不同项目的特殊需要。

         开放的可扩展性。用户可以自行实现框架提供的接口,自行扩展异常处理以及异常日志记录的类,以插件形式供框架调用,以实现最大可能的灵活性。

         性能。因为使用了不少反射技术,在性能上有一定损耗,但使用了单例模式来弥补,只在项目第一出现异常的时候反射加载对象,以后再次调用时则直接使用该对象,对效率基本没有任何损耗了。

 

而多数情况下,以框架提供的默认解决方案已经能够满足普通项目的需要,提供一个功能比较完整的,健壮的异常处理机制:

1)        方便和简化了开发人员及时定位和发现异常原因;

2)        对系统运行状况提供了强有力的数据支持,并使错误信息统一的方式管理,可以改善用户体验;

3)        当项目在用户使用中发现运行错误时,可以记下系统反馈的异常记录编号后于项目开发人员联系,而开发人员可以根据记录编号得到异常发生的详细信息进行分析。有助于缩短项目异常反馈时间。

免责声明:文章转载自《通用异常处理框架(转载)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇DXperience Universal 10.1.6令牌桶、漏斗、冷启动限流在sentinel的应用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Oracle 异常处理

1、什么是异常 在PL/SQL中的一个警告或错误的情形都可被称为异常。包括编译时错误(PLS)和运行时错误(ORA)。一个异常通常包含一个错误代码和错误文本,分别指示异常的编号和具体错误信息。 异常情况处理(EXCEPTION)是用来处理正常执行过程中未预料的事件,程序块的异常处理预定义的错误和自定义错误,由于PL/SQL程序块一旦产生异常而没有指出如何处...

适配器模式之对象适配器

  问题导入:比如有A型螺母和B型螺母,那么用户可以再A型螺母上直接使用按着A型螺母生产的A型螺丝,同样也可以在B型螺母上直接使用按着B型螺母标准生产的B型螺丝。但是由于A型螺母和B型螺母的标准不一样,用户在A型螺母上不能直接使用B型的螺丝,反之也一样。该如何达到这个目的呢?   使用适配器就可以解决这个问题:生产一种“A型螺母适配器”,这种A型螺母适配器...

微信小程序开发四:接口

4.1 网络 发起请求wx.request接口,用于发起 HTTPS 请求。一个微信小程序,同时只能有5个网络请求连接。上传下载wx.uploadFile接口,用于将本地资源上传到开发者服务器。如页面通过 wx.chooseImage 等接口获取到一个本地资源的临时文件路径后,可通过此接口将本地资源上传到指定服务器。客户端发起一个 HTTPS POST...

【原】storm源码之storm代码结构【译】

说明:本文翻译自Storm在GitHub上的官方Wiki中提供的Storm代码结构描述一节Structure of the codebase,希望对正在基于Storm进行源码级学习和研究的朋友有所帮助。 Storm的源码共分为三个不同的层次。 首先,Storm在设计之初就考虑到了兼容多语言开发。Nimbus是一个thrift服务,topologies被定义...

微信公众平台开发步骤(包括自定义菜单、网页授权、分享功能)

第一步:填写服务器配置 第二步:验证服务器地址的有效性 第三步:依据接口文档实现业务逻辑 用户向公众号发送消息时,公众号方收到的消息发送者是一个OpenID,是使用用户微信号加密后的结果,每个用户对每个公众号有一个唯一的OpenID。此外,由于开发者经常有需在多个平台(移动应用、网站、公众帐号)之间共通用户帐号,统一帐号体系的需求,微信开放平台(open....

网络安全开发包介绍

网络安全开发包介绍 引言:网络安全领域的许多网络安全技术,如防火墙、入侵检测、安全扫描、网络嗅探、协议分析、流量统计、网络管理、蜜罐等,其设计和实现都离不开一些网络安全操作,其中一些基本操作都在底层进行,使用频繁,如网络地址的操作、网络接口的操作、数据包的捕获、数据包的构造和发送、网络入侵检测等;还有一些比较复杂的操作,如流量的统计、路由的管理,ARP缓存...