I have a custom exception class derived from AggregateException
. It's shown below.
As you can see, one of the things I do is build a custom Message
, where I truncate the inner exceptions to keep it short.
The problem is, if I catch this exception somewhere and then log ex.Message
to the console, it includes my message, along with the full stack trace of EVERY SINGLE INNER EXCEPTION appended to my message. The thing is, this aggregate exception can sometimes wrap an absolutely insane number of inner exceptions because it deals with a long running big-data job. Application performance is fine, but it is absolutely destroying our logging.
That is, the message ends up coming out as MyMessage + TonsOfExtraCrapGeneratedByDotNet
.
WHY is it overriding my message? Isn't the point of specifying a custom message to give me control? I want it to OVERRIDE - not append.
Here is a runnable .NET Fiddle that reproduces the exact issue: https://dotnetfiddle.net/0x4aD6
public class MyDerivedAggregateException : AggregateException
{
// ... non-relevant code omitted
public MyDerivedAggregateException (
string message,
IEnumerable<Exception> innerExceptions,
bool isEarlyTermination
) : base(BuildMessage(message, innerExceptions.ToList(), isEarlyTermination), innerExceptions)
{
}
private static string BuildMessage(string message, List<Exception> innerExs)
{
var sb = new StringBuilder();
sb.AppendLine(message);
int numToShow = Math.Min(5, innerExs.Count);
int numNotShown = innerExs.Count - numToShow;
for (int i = 0; i < numToShow; i++)
sb.AppendLine($"Exception {i + 1}: {innerExs[i].Message}");
if (numNotShown > 0)
sb.AppendLine($"({numNotShown} more exceptions not shown...)");
return sb.ToString();
}
}
I even tested it by adding the following ToString()
override to my exception, to exactly what .NET thinks the value of the property is:
public override string ToString()
{
File.WriteAllText("C:/Tests/exmessage.txt", Message);
File.WriteAllText("C:/Tests/basemessage.txt", base.Message);
File.WriteAllText("C:/Tests/basetostring.txt", base.ToString());
return "Screw you .NET";
}
In my test, the contents of ALL 3 FILES were the same - they included all of the extra crap, rather than just the value of what I provided to base() on AggregateException.
You can store your message customizations in a field, and return that one by overriding the Message
property.
public class MyDerivedAggregateException : AggregateException
{
private readonly string _message;
public MyDerivedAggregateException(
string message,
IEnumerable<Exception> innerExceptions,
bool isEarlyTermination
) : base(string.Empty, innerExceptions)
{
_message = BuildMessage(message, innerExceptions.ToList());
}
public override string Message => _message;
private static string BuildMessage(string message, List<Exception> innerExs)
{
var sb = new StringBuilder();
sb.AppendLine(message);
// Other message adjustments go here.
return sb.ToString();
}
}