在上一篇中"異常處理--Exception(一)"中,跟你們簡單介紹了一下Exception,也使你們充分的瞭解了Exception管理在一個項目中的重要性,那如何在咱們的項目中處理異常呢?由於我從事的是Web開發,因此我只跟你們討論Web的解決方案,Win的解決方式,還但願同你們一塊兒探討。html
上一章中咱們瞭解了異常發生的緣由,同時也說了不存在沒有bug的程序,任何網站都會遇到各類各樣的問題,不管是大網站仍是小網站都會存在,但大公司和小公司對待異常的態度全然不一樣,一個是主動出擊,一個是守株待兔,咱們是好的開發者,咱們不能坐以待斃,咱們必須主動出擊。好了,廢話少說,切入主題。數據庫
如今網站通常都採用多層開發,多層開發的時候,咱們應該在哪裏處理異常、在拋出異常呢?微軟的意見是類庫的開發人員儘可能不要處理異常,類庫的編寫應該按照正常的邏輯去編寫,固然也有例外,注意事項能夠參見"設計異常解決方案的幾點注意事項",好的,按照規範,咱們應該儘可能在高層進行捕捉和處理,那咱們該怎麼捕捉,捕捉後怎麼處理,捕捉哪些異常呢?雖然微軟提供了不少系統異常,可是這些異常只是負責拋出相關的信息,並無爲記錄下來,或者出現高級異常的時候,及時通知咱們,這樣的作法仍是守株待兔,咱們仍是應該主動的對其進行處理。好在微軟讓咱們能夠自由的建立自定義的Exception,最好是設定一個自定義Exception基類,讓你的其餘自定義Exception都繼承這個類,以便從此更好的擴展。拋出異常實際上是性能消耗很大的操做,可是Richer教父說過,拋出異常的性能和你程序的穩定性相比,就變得很是眇小了。因此咱們仍是偏向於穩定性。由於處理異常的性能消耗,只是在異常發生時才產生,因此性能方面的問題,咱們能夠忽略了。(或許這話比較拗口,但相比系統的性能,我更趨向於系統的穩定)安全
如何建立一個自定義的Exception?post
不得不說微軟考慮的太周到了,要建立一個自定義的Exception是很是簡單的。打開VS,建立一個項目,而後添加一個類,在namespace範圍內,輸入Exception,而後2下Tab,VS就自定幫您建立一個自定義的Exception了。Exception的相關屬性和方法,能夠參見MSDN。不過自動建立的Exception都是繼承System.Exception的,按照微軟當初的設想,自定義的異常應該繼承System.ApplicationException (好笑的是,微軟本身都沒有遵照這個約定)。咱們設定這個做爲咱們的Exception基類MyBaseException。性能
代碼片段:網站
[global::System.Serializable] ui
public class MyBaseException : ApplicationException spa
{ 設計
public MyBaseException() { } 日誌
public MyBaseException(string message) : base(message) { }
public MyBaseException(string message, Exception inner) : base(message, inner) { }
protected MyBaseException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
這就是一個標準的自定義Exception了,至於其它的自定義Exception,應該根據你的項目來進行相關的定義。
在進行其餘定義以前,咱們先來想一想,咱們捕捉這些Exception以後咱們須要作些什麼?咱們須要知道異常發生的各類信息,因此咱們須要Log。Log能方便的讓咱們查閱發生的異常及Log的異常信息。Log有不少方式,大概的有如下幾種:
文本記錄
數據庫記錄
系統事件記錄(Trace)
第三方組件(Log4Net)
這幾種方式各有利弊,能夠根據項目的需求進行選擇,固然你也能夠幾種方式合用,好比咱們默認的是文本記錄方式,可是在建立Log時發生了System.IOException時,咱們就必須選擇其餘的方式進行Log。
Log方式 |
便捷性 |
查閱性 |
安全性 |
結合性 |
文本記錄 |
方便 |
通常 |
低 |
高 |
數據庫記錄 |
通常 |
方便 |
通常 |
高 |
系統事件記錄 |
複雜 |
複雜 |
高 |
通常 |
第三方組件 |
複雜 |
通常 |
通常 |
低 |
我列舉了幾種方式的利弊,你們能夠有條件的選擇。若是你的項目中已經使用第三方組件記錄方式,那我建議您使用它。在我後面的解決方案中,我會利用前2種比較常見的方式相結合。
Log的目的是爲咱們開發者提供發生異常的時間、地點、人物、緣由,因此咱們必須儘量的詳細地記錄,根據一個Exception獲取信息的方法:
Data |
Source |
Dates and Times |
DateTime.Now |
Source of Exception |
Exception.Source |
Type of Exception |
Object.GetType |
Exception Message |
Exception.Message |
Current Method |
Reflection.MethodInfo.GetCurrentMethod |
Machine Name |
Environment.MachineName or Dns.GetHostName |
CurrentIP |
Dns.GetHostByName("host").AddressList[0].Address |
Call Stack |
Exception.StackTrace or Environment.StackTrace |
OS Information |
Environment.OSVersion |
Applcation Domain |
AppDomain.FriendlyName |
Current Assembly |
Reflection.Assembly.GetExecutingAssembly |
Root Error Cause |
Exception.GetBaseException |
Chained Exception |
Exception.InnerException |
Assembly Version |
Included in AssemblyName.FullName |
Thread ID |
AppDomain.GetCurrentThreadId |
Thread User |
Threading.Thread.CurrentPrincipal |
咱們能夠根據上面的表格,構建咱們本身所須要的Log信息。爲了便捷的管理,咱們應該採用同一格式,進行Log。這裏貼一個我寫的信息格式,以供參考:
public static class ExceptionLogFormatHelper
{
public static string ExceptionLogFormatter(Exception ex)
{
StringBuilder sbLog = new StringBuilder("\r\n------------------------------------\r\n");
Exception ochainException = ex;
var currentExceptionIndex = 1;
while (ochainException != null)
{
sbLog.Append("\r\nException " + currentExceptionIndex + " )")
.Append("\r\nException Type:" + ochainException.GetType().FullName)
.Append("\r\nException Source:" + ochainException.Source)
.Append("\r\nException Message:" + ochainException.Message)
.Append("\r\nException Date:" + DateTime.Now)
.Append("\r\nEnvironment Stack:" + System.Environment.StackTrace);
ochainException = ochainException.InnerException;
currentExceptionIndex++;
}
sbLog.Append("\r\n------------------------------------\r\n");
return sbLog.ToString();
}
}
你也能夠根據你本身想要的信息構建這麼一個方法。
這一篇廢話多了點,不過仍是有必要了解下。還介紹了自定義異常的建立,日誌方式的對比,在下一篇,我將介紹通知、異常處理流程和定義本身的一個MyBaseException。