What is the proper way to display the full InnerException?
What is the proper way to show my full InnerException
.
I found that some of my InnerExceptions has another开发者_JS百科 InnerException
and that go's on pretty deep.
Will InnerException.ToString()
do the job for me or do I need to loop through the InnerExceptions
and build up a String
with StringBuilder
?
You can simply print exception.ToString()
-- that will also include the full text for all the nested InnerException
s.
I usually do like this to remove most of the noise:
void LogException(Exception error) {
Exception realerror = error;
while (realerror.InnerException != null)
realerror = realerror.InnerException;
Console.WriteLine(realerror.ToString())
}
Edit: I forgot about this answer and is surprised no one pointed out that you can just do
void LogException(Exception error) {
Console.WriteLine(error.GetBaseException().ToString())
}
Just use exception.ToString()
https://learn.microsoft.com/en-us/dotnet/api/system.exception.tostring#remarks
The default implementation of ToString obtains the name of the class that threw the current exception, the message, the result of calling ToString on the inner exception, and the result of calling Environment.StackTrace. If any of these members is null, its value is not included in the returned string.
If there is no error message or if it is an empty string (""), then no error message is returned. The name of the inner exception and the stack trace are returned only if they are not null.
exception.ToString() will also call .ToString() on that exception's inner exception, and so on...
@Jon's answer is the best solution when you want full detail (all the messages and the stack trace) and the recommended one.
However, there might be cases when you just want the inner messages, and for these cases I use the following extension method:
public static class ExceptionExtensions
{
public static string GetFullMessage(this Exception ex)
{
return ex.InnerException == null
? ex.Message
: ex.Message + " --> " + ex.InnerException.GetFullMessage();
}
}
I often use this method when I have different listeners for tracing and logging and want to have different views on them. That way I can have one listener which sends the whole error with stack trace by email to the dev team for debugging using the .ToString()
method and one that writes a log on file with the history of all the errors that happened each day without the stack trace with the .GetFullMessage()
method.
To pretty print just the Message
s part of deep exceptions, you could do something like this:
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;
}
}
}
This recursively goes through all inner exceptions (including the case of AggregateException
s) to print all Message
property contained in them, delimited by line break.
E.g.
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());
Outer aggr ex occurred.
Inner aggr ex.
Number isn't in correct format.
Unauthorized file access.
Not administrator.
You will need to listen to other Exception properties for more details. For e.g. Data
will have some information. You could do:
foreach (DictionaryEntry kvp in exception.Data)
To get all derived properties (not on base Exception
class), you could do:
exception
.GetType()
.GetProperties()
.Where(p => p.CanRead)
.Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
I do:
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;
}
}
}
This "pretty prints" all inner exceptions and also handles AggregateExceptions and cases where InnerException.Message is the same as Message
If you're using Entity Framework, exception.ToString()
will not give you the details of DbEntityValidationException
exceptions. You might want to use the same method to handle all your exceptions, like:
catch (Exception ex)
{
Log.Error(GetExceptionDetails(ex));
}
Where GetExceptionDetails
contains something like this:
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();
}
If you want information about all exceptions then use exception.ToString()
. It will collect data from all inner exceptions.
If you want only the original exception then use exception.GetBaseException().ToString()
. This will get you the first exception, e.g. the deepest inner exception or the current exception if there is no inner exception.
Example:
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
}
buildup on nawfal 's answer.
when using his answer there was a missing variable aggrEx, I added it.
file 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(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;
}
}
}
This one is better I think
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();
}
This code generates a formatted HTML
representation of the exception:
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 />";
}
精彩评论