Как правильно отобразить полное InnerException?

Как правильно показать мой полный InnerException,

Я обнаружил, что некоторые из моих InnerExceptions имеют другой InnerException и это происходит довольно глубоко.

Будет InnerException.ToString() сделай работу за меня или мне нужно перебрать InnerExceptions и создать String с StringBuilder?

11 ответов

Решение

Вы можете просто напечатать exception.ToString() - это также будет включать в себя полный текст для всех вложенных InnerExceptions.

Я обычно делаю так, чтобы удалить большую часть шума:

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

    Console.WriteLine(realerror.ToString())
}    

Просто используйте exception.ToString()

http://msdn.microsoft.com/en-us/library/system.exception.tostring.aspx

Реализация по умолчанию ToString получает имя класса, выдавшего текущее исключение, сообщение, результат вызова ToString для внутреннего исключения и результат вызова Environment.StackTrace. Если какой-либо из этих членов имеет значение null, его значение не включается в возвращаемую строку.

Если сообщение об ошибке отсутствует или это пустая строка (""), сообщение об ошибке не возвращается. Имя внутреннего исключения и трассировка стека возвращаются, только если они не равны NULL.

exception.ToString() также вызовет.ToString() для внутреннего исключения этого исключения и так далее...

@ Ответ Джона - лучшее решение, когда вам нужна полная детализация (все сообщения и трассировка стека) и рекомендуемое.

Однако могут быть случаи, когда вам просто нужны внутренние сообщения, и для этих случаев я использую следующий метод расширения:

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

Я часто использую этот метод, когда у меня есть разные слушатели для отслеживания и ведения журнала, и я хочу иметь разные взгляды на них. Таким образом, у меня может быть один слушатель, который отправляет всю ошибку с трассировкой стека по электронной почте команде разработчиков для отладки с использованием .ToString() метод и тот, который записывает файл журнала с историей всех ошибок, которые произошли каждый день без трассировки стека с .GetFullMessage() метод.

Чтобы просто распечатать MessageЧасть глубоких исключений, вы можете сделать что-то вроде этого:

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());

Произошло внешнее скопление.
Внутренний агр. Отл.
Номер не в правильном формате.
Несанкционированный доступ к файлам.
Не администратор.


Вам нужно будет прослушать другие свойства исключения для получения более подробной информации. Например, Data будет иметь некоторую информацию. Вы могли бы сделать:

foreach (DictionaryEntry kvp in exception.Data)

Чтобы получить все производные свойства (не на базе Exception класс), вы можете сделать:

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

Если вы используете Entity Framework, 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();
}

Я делаю:

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

Если вы хотите получить информацию обо всех исключениях, используйте 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
}

Наращивание по ответу nawfal.

при использовании его ответа отсутствовала переменная aggrEx, я ее добавил.

файл ExceptionExtenstions.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(e => e.Message.Trim() + "\r\n" + e.StackTrace.Trim() );
            string flattened = String.Join("\r\n\r\n", messages); // <-- the separator here
            return flattened;
        }
    }
}

Это лучше я думаю

      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();
    }

Этот код создает форматированныйHTMLпредставление исключения:

      const string _HTML_TAB = "&nbsp;&nbsp;&nbsp;";

public static string ToHtmlString(this Exception ex, int level = 0)
{
    string message = GetText("Message", ex.Message, level);

    if (ex.InnerException != null && level < 30)
    {
        message += ToHtmlString(ex.InnerException, level + 1);
    }
    else
    {
        message += GetText("StackTrace", ex.StackTrace, level); ;
        message += GetText("Source", ex.Source, level); ;
        message += GetText("TargetSite", ex.TargetSite.ToString(), level);
    }

    return message;
}

private static string GetText(string headline, string text, int level)
{
    var indentText = string.Join(_HTML_TAB, new string[level + 1]);
    var newLine = $"<br />{indentText}{_HTML_TAB}";

    return $"{indentText}<b>{headline}</b>{newLine}"
            + $"{text.Replace(Environment.NewLine, newLine)}<br /><br />";
}
Другие вопросы по тегам