Rethrowing exceptions and preserving the full call stack trace
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.
Rethrowing exceptions and preserving the full call stack trace的更多相关文章
- The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
好久没有冒泡了,最近在新环境上搭建应用时,启动报错: INFO: Illegal access: this web application instance has been stopped alre ...
- java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE 的理解
[2013-12-06 11:06:21,715] [C3P0PooledConnectionPoolManager[identityToken->2tl0n98y1iwg7cbdzzq7a|7 ...
- XDebug 自动开启PHP Stack Trace, 导致PHP Log 超1G
昨天早上突然发现测试服务器空间满了,用du挨个文件夹查看,发现是php debug log占地极大,有的log直接有1G,打开后发现极其多的php stack trace. 立刻到主服务器查看,主服务 ...
- dump 分析模式之 INCORRECT STACK TRACE - djm2005dy的专栏 - 博客频道 - CSDN.NET
Dump 分析模式之 INCORRECT STACK TRACE dump 分析模式之 INCORRECT STACK TRACE 翻译自 MDA-Anthology Page288 初学者常犯的错 ...
- Eclipse下Android开发错误之Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace
升级了Android版本后,在运行应用时提示: [2013-11-27 10:37:35 - Dex Loader] Unable to execute dex: java.nio.BufferOve ...
- Android -- java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
使用Bundle 的getParcelableArray 出现了以下错误: Class not found when unmarshallingjava.lang.ClassNotFoundExcep ...
- 让Xcode的 stack trace信息可读
让Xcode的 stack trace信息可读 昨天在写 iOS 代码的时候,调试的时候模拟器崩溃了.异常停在了如下整个 main 函数的入口处: int main(int argc, char *a ...
- 项目跑起来之后,一会儿后台就会报错Illegal access: this web application instance has been stopped already. Could not load [com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask]. The following stack trace
一月 24, 2016 6:42:54 下午 org.apache.catalina.loader.WebappClassLoaderBase checkStateForResourceLoading ...
- Java 抓取 thread dump (Full Thread Stack Trace) 方法汇总
顾名思义,表示一个时间点上,显示进程里面每一个线程的 stack trace,以及线程之间关联,比如等待 常用来定位一些 不响应,CPU 很高,内存使用很高问题 汇总表格如下 工具 操作系统 Java ...
随机推荐
- not子查询中有null值的时候 not in 会失效
not in子查询中有null值的时候 not in 会失效 但是 in 的子查询中有null的 不会失效
- Spark 1.6以后的内存管理机制
Spark 内部管理机制 Spark的内存管理自从1.6开始改变.老的内存管理实现自自staticMemoryManager类,然而现在它被称之为"legacy". " ...
- selenium 关于富文本的处理
http://www.cnblogs.com/xiaobaichuangtianxia/p/5889999.html
- boost相关
1 boost 常用函数 <1> tcp跟udp的收发函数名 tcp收发 async_write async_read async_read_until udp收发 async_send_ ...
- DIJ产品系列
- 为什么SqlTransaction.Rollback会抛出SqlException(11,-2)(即SQL超时异常)
// System.Data.SqlClient.SqlTransaction public override void Rollback() { if (this.IsYukonPartia ...
- R&S学习笔记(三)
1.GRE OVER IPv4 GRE协议栈:IPSEC只支持TCP/IP协议的网络,GRE则支持多协议,不同的网络类型.(如IPX,APPLETALK):通常IPSEC over gre结合使用, ...
- 基于Windows服务器集群的Redis主从配置指南
前段时间一个项目因并发量大.因防止宕机做了主从备份,首页的表连接查询又非常的耗时.故此拿出利器Redis缓存这个查询结果,并随着用户操作而更新. 因官方目前只有linux版,Windows版下载: ...
- tomcat源码剖析
最近看Tomcat的源码的节奏还算是挺紧凑的,给人的感觉,tomcat的代码相对以前读的jetty的代码显得更有条理一些...当然这也是有可能是因为自己看的jetty的版本是比较老的,而看的Tomca ...
- 世界国家 的数据库sql
, '中国', 'CHINA'); , '阿尔巴尼亚', 'ALB'); , '阿尔及利亚', 'DZA'); , '阿富汗', 'AFG'); , '阿根廷', 'ARG'); , '阿拉伯联合酋长 ...