Как правильно отобразить полное InnerException?
Как правильно показать мой полный InnerException
,
Я обнаружил, что некоторые из моих InnerExceptions имеют другой InnerException
и это происходит довольно глубоко.
Будет InnerException.ToString()
сделай работу за меня или мне нужно перебрать InnerExceptions
и создать String
с StringBuilder
?
11 ответов
Вы можете просто напечатать exception.ToString()
- это также будет включать в себя полный текст для всех вложенных InnerException
s.
Я обычно делаю так, чтобы удалить большую часть шума:
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;
}
}
}
Это рекурсивно проходит через все внутренние исключения (включая случай AggregateException
s) распечатать все 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 = " ";
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 />";
}