zoukankan      html  css  js  c++  java
  • Rethrowing exceptions and preserving the full call stack trace

    refer:http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx

    http://geekswithblogs.net/sdorman/archive/2007/08/20/Difference-between-quotthrowquot-and-quotthrow-exquot-in-.NET.aspx

    Did you know that depending on the way you rethrow exceptions you may lose important information? There are already several blog posts that explain and demonstrate the difference between throw and throw ex. I'm realizing only now that none of the two solutions yields the complete call stack trace information!

    Let's see what the problem is and I'll show you the real solution.

    I'll use the following method to generate an exception:

    private static void BadWork()
    {
      int i = 0;
      int j = 12 / i; // Line 10: DivideByZeroException
      int k = j + 1;
    }

    Let's consider what happens if we call BadWork and rethrow the exception with throw ex as follows:

    try
    {
      BadWork();
    }
    catch (Exception ex)
    {
      // do something
      // ...
      throw ex; // Line 24
    }

    Here is the call stack trace that we get in this case:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.WithThrowEx() in Program.cs:line 24
       at Program.Main(String[] args) in Program.cs:line 88

    Line 24 is where throw ex is, not where the exception was thrown.

    Let's now replace throw ex by throw:

    try
    {
      BadWork();
    }
    catch
    {
      // do something
      // ...
      throw; // Line 38
    }


    This time, here is the call stack trace:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.BadWork() in Program.cs:line 10
       at Program.WithThrow() in Program.cs:line 38
       at Program.Main(String[] args) in Program.cs:line 89

    As you can see, we get one additional stack frame this time. Line 10 is where the exception was thrown, which is important information because this is the only information that identifies where the exception actually happened.

    This shows that it's better to use throw rather than throw ex if you want the full stack trace information to be preserved. However, there are cases where throw is not enough. In the following example, throw does not preserve the full stack trace:

    try
    {
      int i = 0;
      int j = 12 / i; // Line 47
      int k = j + 1;
    }
    catch
    {
      // do something
      // ...
      throw; // Line 54
    }

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.WithThrowIncomplete() in Program.cs:line 54
       at Program.Main(String[] args) in Program.cs:line 106

    This time, you can see that information is lost again. Line 54 is where throw is, not where the exception was thrown.

    To preserve the full call stack information, you need to use the following method:

    private static void PreserveStackTrace(Exception exception)
    {
      MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
        BindingFlags.Instance | BindingFlags.NonPublic);
      preserveStackTrace.Invoke(exception, null);
    }

    This method can be used as follows: 

    try
    {
      int i = 0;
      int j = 12 / i; // Line 78
      int k = j + 1;
    }
    catch (Exception ex)
    {
      // do something
      // ...
      PreserveStackTrace(ex);
      throw; // Line 86
    }

    Here is the new call stack information you get with the above code:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.WithThrowAndStackTracePreservation() in Program.cs:line 78
       at Program.WithThrowAndStackTracePreservation() in Program.cs:line 86
       at Program.Main(String[] args) in Program.cs:line 110

    Here is the call stack information you get with throw ex and a call to PreserveStackTrace:

    Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
       at Program.BadWork() in Program.cs:line 10
       at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 62
       at Program.WithThrowExAndStackTracePreservation() in Program.cs:line 69
       at Program.Main(String[] args) in Program.cs:line 109

    Here we get the full call stack information. Lines 78 and 10 are where the exceptions were thrown. To my knowledge, this is the only way to get complete call stack information in your logs. Without it, it may be difficult to hunt down some bugs.
    It's worth noting that if you call PreserveStackTrace, then you can use throw or throw ex and you'll equally get the full stack trace information.

    I found this useful trick on Chris Taylor's blog. If you want to use this with .NET 1, you should refer to Chris' post because it seems that the InternalPreserveStackTrace method didn't exist before .NET 2.0.

    The complete source code is attached to this post.

  • 相关阅读:
    Django----抽屉项目 笔记
    Django验证码【附源码】
    C语言--循环控制结构
    javascript 流程控制及函数
    yield和yield from
    python面试题(转)
    断言assert用法
    javascript基本语法
    python经典一百道习题(转自奶酪博客)
    functools模块中的函数
  • 原文地址:https://www.cnblogs.com/Refresh-air/p/3315103.html
Copyright © 2011-2022 走看看