.NET項目中有這樣一個場景:異步
使用反射處理全部業務調用,在反射調用點使用try-catch集中處理異常,並將異常信息記錄到日誌。其中日誌記錄是異步的。spa
問題:日誌
記錄到日誌中的異常的StackTrace和有時候和Debug時拋出的異常的StackTrace不同。code
緣由:blog
因爲記錄日誌是異步的,若是記錄日誌發生在throw以前,記錄到日誌中的異常的StackTrace就是正確的(異常真正發生點到throw點的全部StackTrace);若是記錄日誌發生在throw以後,記錄到日誌中的異常的StackTrace就是不正確的(只有當前throw點到代碼入口點的StackTrace);
由於拋出的異常(或是該異常的引用)恰好是用來記錄日誌用的異常實例,且throw動做會對拋出的異常(和記錄日誌的異常實例是同一個引用)從新生成StackTrace,因此記錄日誌用的異常的StackTrace被從新生成了。遞歸
(備註1:StackTrace是Exception的只讀屬性,throw時會對扔出的異常生成StackTrace)
(備註2:若是記錄日誌是同步的且先記錄日誌後throw,則應該不會發生這個問題)同步
解決方案:string
♦用到的方法:io
1 // 深賦值異常鏈上的全部異常,可是會丟失異常鏈上非葉子節點的異常的StackTrace。最內部的異常信息會完整保留,由於最內部的異常不是副本 2 public static Exception CloneInnerExceptionRecursive(Exception ex) 3 { 4 Exception exception = null; 5 6 if (ex.InnerException == null) 7 { 8 exception = ex; 9 } 10 else 11 { 12 Stack<Exception> stack = new Stack<Exception>(); 13 stack.Push(ex); 14 Exception innerException = ex.InnerException; 15 while (innerException != null) 16 { 17 if (innerException.InnerException == null) 18 { 19 break; 20 } 21 stack.Push(innerException); 22 innerException = innerException.InnerException; 23 } 24 25 try 26 { 27 Exception chain = innerException; 28 Exception temp = null; 29 while (stack.Count > 0) 30 { 31 temp = stack.Pop(); 32 chain = CloneException(temp, chain); 33 } 34 exception = chain; 35 } 36 catch 37 { 38 } 39 40 if (exception == null || exception.InnerException == null) 41 { 42 exception = ex.InnerException; 43 } 44 } 45 46 return exception; 47 }
1 // 複製異常 2 public static Exception CloneException(Exception ex, Exception inner) 3 { 4 return (Exception)ex.GetType().GetConstructor(new Type[] { typeof(string), typeof(Exception) }).Invoke(new object[] { ex.Message, inner }); 5 }