【问题标题】:What is the proper way to display the full InnerException?显示完整 InnerException 的正确方法是什么?
【发布时间】:2011-08-21 04:49:45
【问题描述】:

显示我完整的InnerException 的正确方法是什么。

我发现我的一些 InnerExceptions 有另一个 InnerException,而且这种情况非常深入。

InnerException.ToString() 会为我完成这项工作,还是我需要遍历InnerExceptions 并用StringBuilder 建立一个String

【问题讨论】:

  • 为什么要显示内部异常??
  • @Akram 因为大多数时候有趣的是内部异常。一个示例是 XmlSerializer,只要出现问题,它就会抛出 InvalidOperationException。问题出在内部异常中。
  • @AkramShahda 好吧,也许您想在日志记录中使用此方法?

标签: c# exception inner-exception


【解决方案1】:

您可以简单地打印exception.ToString() -- 这还将包括所有嵌套InnerExceptions 的全文。

【讨论】:

  • 这也包括大量其他的废话,不仅仅是异常消息和内部异常消息
  • 为了简洁起见,您实际上并不需要 .ToString(),只需使用异常即可。
  • @AlexStephens 你是对的,但前提是你出于某种原因隐式转换为“字符串”,例如前面的字符串:“bla”+ 异常
  • 仅供参考:它不会为内部异常调用自定义 ToString 方法,详见 Why doesn't System.Exception.ToString call virtual ToString for inner exceptions?
  • 适用于大多数情况,但如果您使用的是实体框架,则不会在 DbEntityValidationException 中包含 ValidationErrors。请参阅下面的回复。
【解决方案2】:

我通常这样做是为了消除大部分噪音:

void LogException(Exception error) {
    Exception realerror = error;
    while (realerror.InnerException != null)
        realerror = realerror.InnerException;

    Console.WriteLine(realerror.ToString())
}    

编辑:我忘记了这个答案,很惊讶没有人指出你可以这样做

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    

【讨论】:

  • 这个方法隐藏了除了最深的内部异常之外的所有东西。如果那是像“除以零”这样的普通错误,那么它发生在哪里以及导致它的原因就不清楚了。显然,完整的堆栈跟踪通常是一种凌乱的矫枉过正,但仅读取内部异常是另一个极端。 user3016982 的回答要好得多。您可以在堆栈中获得每条异常消息,而不会留下令人讨厌的痕迹。
  • @JamesHoux 哪个是“user3016982”的答案?在这里找不到他。
  • 用户 3016982 是 ThomazMoura,请参阅:stackoverflow.com/users/3016982/thomazmoura
  • @JamesHoux,内部异常有一个完整的堆栈跟踪,显示错误发生的位置以及导致错误的原因。不明白您从删除的堆栈跟踪中获得了哪些额外信息。异常消息是另一回事,收集所有异常消息可能很有用。
  • 你为什么不直接使用error.GetBaseException()。我相信这也是一样的......
【解决方案3】:

只需使用exception.ToString()

https://docs.microsoft.com/en-us/dotnet/api/system.exception.tostring#remarks

ToString的默认实现获取抛出当前异常的类名、消息、内部异常调用ToString的结果、调用Environment.StackTrace的结果。如果这些成员中的任何一个为 null,则其值不包含在返回的字符串中。

如果没有错误信息或者是空字符串 (""),则不返回错误信息。内部异常的名称和堆栈跟踪仅在它们不为空时才返回。

exception.ToString() 还将在该异常的内部异常上调用 .ToString(),依此类推...

【讨论】:

    【解决方案4】:

    @Jon 的答案是您想要完整详细信息(所有消息和堆栈跟踪)和推荐的最佳解决方案。

    但是,在某些情况下,您可能只需要内部消息,对于这些情况,我使用以下扩展方法:

    public static class ExceptionExtensions
    {
        public static string GetFullMessage(this Exception ex)
        {
            return ex.InnerException == null 
                 ? ex.Message 
                 : ex.Message + " --> " + ex.InnerException.GetFullMessage();
        }
    }
    

    当我有不同的监听器用于跟踪和记录并希望对它们有不同的看法时,我经常使用这种方法。这样我就可以有一个监听器通过电子邮件将带有堆栈跟踪的整个错误发送给开发团队以使用.ToString() 方法进行调试,另一个监听器将每天发生的所有错误的历史记录写入日志文件而没有使用.GetFullMessage() 方法进行堆栈跟踪。

    【讨论】:

    • 仅供参考,如果 exAggregateException,则此输出中不会包含任何内部异常
    • 这应该是一种常用的 .NET 方法。每个人都应该使用它。
    【解决方案5】:

    要仅打印深度异常中的 Messages 部分,您可以执行以下操作:

    public static string ToFormattedString(this Exception exception)
    {
        IEnumerable<string> messages = exception
            .GetAllExceptions()
            .Where(e => !String.IsNullOrWhiteSpace(e.Message))
            .Select(e => e.Message.Trim());
        string flattened = String.Join(Environment.NewLine, messages); // <-- the separator here
        return flattened;
    }
    
    public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
    {
        yield return exception;
    
        if (exception is AggregateException aggrEx)
        {
            foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
            {
                yield return innerEx;
            }
        }
        else if (exception.InnerException != null)
        {
            foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
            {
                yield return innerEx;
            }
        }
    }
    

    这递归地遍历所有内部异常(包括AggregateExceptions 的情况)以打印其中包含的所有Message 属性,由换行符分隔。

    例如

    var outerAggrEx = new AggregateException(
        "Outer aggr ex occurred.",
        new AggregateException("Inner aggr ex.", new FormatException("Number isn't in correct format.")),
        new IOException("Unauthorized file access.", new SecurityException("Not administrator.")));
    Console.WriteLine(outerAggrEx.ToFormattedString());
    

    发生了外部聚集。
    内聚集前。
    数字格式不正确。
    未经授权的文件访问。
    不是管理员。


    您需要听取其他 Exception 属性以获取更多详细信息。例如Data 会有一些信息。你可以这样做:

    foreach (DictionaryEntry kvp in exception.Data)
    

    要获取所有派生属性(不是基于 Exception 类),您可以这样做:

    exception
        .GetType()
        .GetProperties()
        .Where(p => p.CanRead)
        .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
    

    【讨论】:

    • +1,这和我做的几乎一模一样。请考虑寻找实现IEnumerable&lt;Exception&gt; 的属性,而不是硬编码AggregrateException 来处理其他类似类型。还要排除p.IsSpecialNamepi.GetIndexParameters().Length != 0 以避免出现问题。在输出中包含异常类型名称也是一个好主意
    • @adrianm 关于属性信息检查的要点。关于检查异常集合,这完全取决于您要在哪里画线。当然也可以这样做..
    【解决方案6】:

    我愿意:

    namespace System {
      public static class ExtensionMethods {
        public static string FullMessage(this Exception ex) {
          if (ex is AggregateException aex) return aex.InnerExceptions.Aggregate("[ ", (total, next) => $"{total}[{next.FullMessage()}] ") + "]";
          var msg = ex.Message.Replace(", see inner exception.", "").Trim();
          var innerMsg = ex.InnerException?.FullMessage();
          if (innerMsg is object && innerMsg!=msg) msg = $"{msg} [ {innerMsg} ]";
          return msg;
        }
      }
    }
    

    这个“漂亮打印”所有内部异常,还处理 AggregateExceptions 和 InnerException.Message 与 Message 相同的情况

    【讨论】:

      【解决方案7】:

      如果您需要有关所有异常的信息,请使用exception.ToString()。它将从所有内部异常中收集数据。

      如果您只想要原始异常,请使用exception.GetBaseException().ToString()。这将为您提供第一个例外,例如如果没有内部异常,则为最深的内部异常或当前异常。

      例子:

      try {
          Exception ex1 = new Exception( "Original" );
          Exception ex2 = new Exception( "Second", ex1 );
          Exception ex3 = new Exception( "Third", ex2 );
          throw ex3;
      } catch( Exception ex ) {
          // ex => ex3
          Exception baseEx = ex.GetBaseException(); // => ex1
      }
      

      【讨论】:

        【解决方案8】:

        如果您使用的是实体框架,exception.ToString() 不会向您提供DbEntityValidationException 异常的详细信息。您可能希望使用相同的方法来处理所有异常,例如:

        catch (Exception ex)
        {
           Log.Error(GetExceptionDetails(ex));
        }
        

        GetExceptionDetails 包含这样的内容:

        public static string GetExceptionDetails(Exception ex)
        {
            var stringBuilder = new StringBuilder();
        
            while (ex != null)
            {
                switch (ex)
                {
                    case DbEntityValidationException dbEx:
                        var errorMessages = dbEx.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage);
                        var fullErrorMessage = string.Join("; ", errorMessages);
                        var message = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
        
                        stringBuilder.Insert(0, dbEx.StackTrace);
                        stringBuilder.Insert(0, message);
                        break;
        
                    default:
                        stringBuilder.Insert(0, ex.StackTrace);
                        stringBuilder.Insert(0, ex.Message);
                        break;
                }
        
                ex = ex.InnerException;
            }
        
            return stringBuilder.ToString();
        }
        

        【讨论】:

          【解决方案9】:

          基于 nawfal 的回答。

          当使用他的答案时,缺少一个变量 aggrEx,我添加了它。

          文件 ExceptionExtensions.class:

          // example usage:
          // try{ ... } catch(Exception e) { MessageBox.Show(e.ToFormattedString()); }
          
          using System;
          using System.Collections.Generic;
          using System.Linq;
          using System.Text;
          using System.Threading.Tasks;
          
          namespace YourNamespace
          {
              public static class ExceptionExtensions
              {
          
                  public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
                  {
                      yield return exception;
          
                      if (exception is AggregateException )
                      {
                          var aggrEx = exception as AggregateException;
                          foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
                          {
                              yield return innerEx;
                          }
                      }
                      else if (exception.InnerException != null)
                      {
                          foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
                          {
                              yield return innerEx;
                          }
                      }
                  }
          
          
                  public static string ToFormattedString(this Exception exception)
                  {
                      IEnumerable<string> messages = exception
                          .GetAllExceptions()
                          .Where(e => !String.IsNullOrWhiteSpace(e.Message))
                          .Select(exceptionPart => exceptionPart.Message.Trim() + "\r\n" + (exceptionPart.StackTrace!=null? exceptionPart.StackTrace.Trim():"") );
                      string flattened = String.Join("\r\n\r\n", messages); // <-- the separator here
                      return flattened;
                  }
              }
          }
          

          【讨论】:

          • 我有一个例外,因为:e.StackTrace == null
          • 我已经更新了 .Select(e => e.Message.Trim() + "\r\n" + (e.StackTrace!=null?StackTrace.Trim():"") ) ;也许这有帮助
          【解决方案10】:

          我觉得这个更好

          public static string GetCompleteMessage(this Exception error)
              {
                  System.Text.StringBuilder builder = new StringBuilder();
                  Exception realerror = error;
                  builder.AppendLine(error.Message);
                  while (realerror.InnerException != null)
                  {
                      builder.AppendLine(realerror.InnerException.Message);
                      realerror = realerror.InnerException;
                  }
                  return builder.ToString();
              }
          

          【讨论】:

            猜你喜欢
            • 2015-02-22
            • 2013-01-03
            • 2017-09-14
            • 1970-01-01
            • 1970-01-01
            • 2011-03-25
            • 2010-11-21
            • 2016-11-19
            • 2014-04-28
            相关资源
            最近更新 更多