记录到日志中的异常栈缺失

项目中有这样一个场景:

使用反射处理所有业务调用,在反射调用点使用try-catch集中处理异常,并将异常信息记录到日志。其中日志记录是异步的。

问题:

记录到日志中的异常的StackTrace和有时候和Debug时抛出的异常的StackTrace不一样。

原因:

由于记录日志是异步的,如果记录日志发生在throw之前,记录到日志中的异常的StackTrace就是正确的(异常真正发生点到throw点的所有StackTrace);如果记录日志发生在throw之后,记录到日志中的异常的StackTrace就是不正确的(只有当前throw点到代码入口点的StackTrace);
因为抛出的异常(或是该异常的引用)刚好是用来记录日志用的异常实例,且throw动作会对抛出的异常(和记录日志的异常实例是同一个引用)重新生成StackTrace,所以记录日志用的异常的StackTrace被重新生成了。

(备注1:StackTrace是Exception的只读属性,throw时会对扔出的异常生成StackTrace)
(备注2:如果记录日志是同步的且先记录日志后throw,则应该不会发生这个问题)

解决方案:

  • 如果只需要记录最内部的异常(异常真正发生点),可在catch中先将内部异常剥出来,将该异常记录日志,然后将该异常复制一个副本,throw异常的副本;
  • 如果要记录完整的调用栈(包含反射异常等)且抛出最内部异常,可以先记录该异常(包含完整调用栈),然后将最内部异常剥出来,复制该异常的一个副本,throw异常的副本;
  • 如果要记录完整的调用栈且抛出原始异常,可以先记录该异常(包含完整调用栈),然后递归复制异常链上的所有异常的副本并按原顺序合成新的异常链(原始异常链的深复制副本),throw异常链的副本。但是该做法会丢失异常链中间节点的所有StackTrace;

♦用到的方法:

 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         }
时间: 2024-11-05 22:55:46

记录到日志中的异常栈缺失的相关文章

通用权限管理系统记录登录日志中的一个问题

最近,在使用通用权限管理系统开发一个项目,数据库是MSSQL,登录时出现一个错误,发现是记录登录日志时出现的错误. 由于每次登录,都会记录登录日志,就是在记录日志时出现了错误. 先把错误截图 可以看出与记录登录日志的表BaseLoginLog的ID有关系,打开该表 发现该表的ID是nvarchar(50)类型,查询一下该表的数据,截图如下 可看到,ID实际存储的是GUID类型数据,然后我在调试底层代码,找到三个地方: 1 /// <summary> 2 /// 记录登录日志 3 /// <

ThreadLocal 在记录操作日志中的应用

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多.可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量 在HandlerInterceptor的preHandle 中可以截取crud等操作的一些url public class PlatformLogInterceptor implements HandlerInterceptor { private Logger log = LoggerF

【ThinkingInJava】25、将异常输出记录到日志

/** * 书本:<Thinking In Java> * 功能:将异常输出记录到日志中. * 文件:LoggingExceptions.java * 时间:2015年4月8日21:11:51 * 作者:cutter_point */ package Lesson12_error_handling_with_exceptions; import java.io.PrintWriter; import java.io.StringWriter; import java.util.logging.

Laravel 5.4 中的异常处理器和HTTP异常处理实例教程

错误和异常是处理程序开发中不可回避的议题,在本地开发中我们往往希望能捕获程序抛出的异常并将其显示打印出来,以便直观的知道程序在哪里出了问题并予以解决,而在线上环境我们不希望将程序错误或异常显示在浏览器中(出于安全考虑),这个时候我们仍然要捕获异常,只不过不是显示到浏览器中,而是记录到日志中,方便日后排查问题. 百牛信息技术bainiu.ltd整理发布于博客园 Laravel当然支持PHP原生的错误和异常处理,但是在此基础上进行了一些封装处理,从而更方便在不同开发环境切换以及对错误和异常的处理.

C# 使用Trace记录程序日志

在程序开发中,我们通常需要记录程序运行的状态,在程序部署后,发生的异常可以记录在日志中,便于发现程序潜在的问题.在.NET平台,有很多优秀的日志类库,例如Log4Net.如果程序很小,我们可以自己通过C#的Trace类来实现一个基本的日志记录功能.下面直接看代码: public class TraceHelper { private static TraceHelper _traceHelper; private TraceHelper() { } public static TraceHelp

Laravel 5.1 中的异常处理器和HTTP异常处理 abort()

原文  http://laravelacademy.org/post/1867.html 错误和异常是处理程序开发中不可回避的议题,在本地开发中我们往往希望能捕获程序抛出的异常并将其显示打印出来,以便直观的知道程序在哪里出了问题并予以解决,而在线上环境我们不希望将程序错误或异常显示在浏览器中(出于安全考虑),这个时候我们仍然要捕获异常,只不过不是显示到浏览器中,而是记录到日志中,方便日后排查问题. Laravel当然支持PHP原生的错误和异常处理,但是在此基础上进行了一些封装处理,从而更方便在不

在Main方法中设置异常的最后一次捕捉

在做Winfrom程序时,有时会遇到一个异常,可是这个异常不知道在什么地方发生的,程序会自动关闭,然后什么也没有了,在网上找到了一种方法,用来捕捉这种异常. 出现这种情况的原因是在程序中某些地方考虑不周全,有异常的情况没有考虑到,但是CLR不会在出错时给出提示(注:有些错误没有捕捉的话会自动弹出错误框,让用户选择关闭程序还是继续),所以就出了事了... 这时有一种方法来得到这种异常: 1 /// <summary> 2 /// 应用程序的主入口点. 3 /// </summary>

Python 处理异常栈模块——traceback 模块

异常捕捉 通常我们在项目中,针对异常的捕捉会使用 try + except,基本形式如下: try: # 主代码 except IndexError as e: # 索引异常时执行这里 logger.debug(e) except KeyError as e: # 关键字异常时执行这里 logger.debug(e) except ValueError as e: # 值异常时执行这里 logger.debug(e) except Exception as e: # 万能异常,若出现了与上述指定

通过Jenkins调用api设置Kong的权重不正确日志中出现no memory

[背景描述]:通过调用kongAPI编写脚本,实现对kong的target权重的设置,即流量的摘除与添加操作. [问题描述]:通过Jenkins构建完成后,出现两种异常情况:1.kong的target少了一个节点,比如本应该是3个节点,最后只添加了2个节点2.kong的target权重非100,即有时候权限值居然是1日志中出现异常报错: 127.0.0.1 - - [26/Jun/2019:04:04:24 +0000] "POST /config?check_hash=1 HTTP/1.1&q