記錄到日誌中的異常棧缺失

.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

  • 若是隻須要記錄最內部的異常(異常真正發生點),可在catch中先將內部異常剝出來,將該異常記錄日誌,而後將該異常複製一個副本,throw異常的副本;
  • 若是要記錄完整的調用棧(包含反射異常等)且拋出最內部異常,能夠先記錄該異常(包含完整調用棧),而後將最內部異常剝出來,複製該異常的一個副本,throw異常的副本;
  • 若是要記錄完整的調用棧且拋出原始異常,能夠先記錄該異常(包含完整調用棧),而後遞歸複製異常鏈上的全部異常的副本並按原順序合成新的異常鏈(原始異常鏈的深複製副本),throw異常鏈的副本。可是該作法會丟失異常鏈中間節點的全部StackTrace;

 

♦用到的方法: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         }
相關文章
相關標籤/搜索