Skip to content

Commit 670c298

Browse files
authored
ExceptionLayoutRenderer - Added FlattenException option (#4153)
1 parent ded8dd5 commit 670c298

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,21 @@ public string InnerFormat
179179
/// <summary>
180180
/// Gets or sets whether to render innermost Exception from <see cref="Exception.GetBaseException()"/>
181181
/// </summary>
182+
[DefaultValue(false)]
182183
public bool BaseException { get; set; }
183184

185+
#if !NET3_5 && !SILVERLIGHT4
186+
/// <summary>
187+
/// Gets or sets whether to collapse exception tree using <see cref="AggregateException.Flatten()"/>
188+
/// </summary>
189+
#else
190+
/// <summary>
191+
/// Gets or sets whether to collapse exception tree using AggregateException.Flatten()
192+
/// </summary>
193+
#endif
194+
[DefaultValue(true)]
195+
public bool FlattenException { get; set; } = true;
196+
184197
/// <summary>
185198
/// Gets the formats of the output of inner exceptions to be rendered in target.
186199
/// </summary>
@@ -226,7 +239,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent)
226239
#if !NET3_5 && !SILVERLIGHT4
227240
if (logEvent.Exception is AggregateException aggregateException)
228241
{
229-
primaryException = GetPrimaryException(aggregateException);
242+
primaryException = FlattenException ? GetPrimaryException(aggregateException) : aggregateException;
230243
AppendException(primaryException, Formats, builder, aggregateException);
231244
if (currentLevel < MaxInnerExceptionLevel)
232245
{
@@ -437,15 +450,17 @@ protected virtual void AppendHResult(StringBuilder sb, Exception ex)
437450
}
438451
#endif
439452
}
453+
440454
private void AppendData(StringBuilder builder, Exception ex, Exception aggregateException)
441455
{
442-
if (aggregateException?.Data?.Count > 0)
456+
if (aggregateException?.Data?.Count > 0 && !ReferenceEquals(ex, aggregateException))
443457
{
444458
AppendData(builder, aggregateException);
445459
builder.Append(Separator);
446460
}
447461
AppendData(builder, ex);
448462
}
463+
449464
/// <summary>
450465
/// Appends the contents of an Exception's Data property to the specified <see cref="StringBuilder" />.
451466
/// </summary>

tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ public void AggregateExceptionWithExceptionDataSingleTest()
783783
Assert.Contains(string.Format(ExceptionDataFormat, exceptionDataKey, exceptionDataValue), lastMessage);
784784
Assert.Contains(string.Format(ExceptionDataFormat, aggregateExceptionDataKey, aggregateExceptionDataValue), lastMessage);
785785
}
786+
786787
[Fact]
787788
public void CustomExceptionProperties_Layout_Test()
788789
{
@@ -819,6 +820,40 @@ public void BaseExceptionTest()
819820
AssertDebugLastMessage("debug1", "Goodbye World");
820821
}
821822

823+
#if NET3_5
824+
[Fact(Skip = "NET3_5 not supporting AggregateException")]
825+
#else
826+
[Fact]
827+
#endif
828+
public void RecursiveAsyncExceptionWithoutFlattenException()
829+
{
830+
var recursionCount = 3;
831+
Func<int> innerAction = () => throw new ApplicationException("Life is hard");
832+
var t1 = System.Threading.Tasks.Task<int>.Factory.StartNew(() =>
833+
{
834+
return NestedFunc(recursionCount, innerAction);
835+
});
836+
837+
try
838+
{
839+
t1.Wait();
840+
}
841+
catch (AggregateException ex)
842+
{
843+
var layoutRenderer = new ExceptionLayoutRenderer() { Format = "ToString", FlattenException = false };
844+
var logEvent = LogEventInfo.Create(LogLevel.Error, null, null, (object)ex);
845+
var result = layoutRenderer.Render(logEvent);
846+
int needleCount = 0;
847+
int foundIndex = result.IndexOf(nameof(NestedFunc), 0);
848+
while (foundIndex >= 0)
849+
{
850+
++needleCount;
851+
foundIndex = result.IndexOf(nameof(NestedFunc), foundIndex + nameof(NestedFunc).Length);
852+
}
853+
Assert.True(needleCount >= recursionCount, $"{needleCount} too small");
854+
}
855+
}
856+
822857
private class ExceptionWithBrokenMessagePropertyException : NLogConfigurationException
823858
{
824859
public override string Message => throw new Exception("Exception from Message property");
@@ -885,6 +920,21 @@ private Exception GetNestedExceptionWithStackTrace(string exceptionMessage)
885920
}
886921
}
887922

923+
private int NestedFunc(int recursion, Func<int> innerAction)
924+
{
925+
try
926+
{
927+
if (recursion-- == 0)
928+
return System.Threading.Tasks.Task<int>.Factory.StartNew(() => innerAction.Invoke())
929+
.Result;
930+
return NestedFunc(recursion, innerAction);
931+
}
932+
catch
933+
{
934+
throw; // Just to make the method complex, and avoid inline
935+
}
936+
}
937+
888938
private Exception GetExceptionWithoutStackTrace(string exceptionMessage)
889939
{
890940
return new CustomArgumentException(exceptionMessage, "exceptionMessage");

0 commit comments

Comments
 (0)