Skip to content

ExceptionLayoutRenderer doesn't append AggregateException's own stack trace when using ToString RenderingFormat #4150

@liorss18

Description

@liorss18
  • NLog version: 4.7.5

  • Platform: .NET Core, but by looking on the source code it seems it happens on other platforms as well.

  • Current NLog config

<?xml version="1.0" ?>
<nlog>
<targets>
    <target
       name="consoleError"
       type="ColoredConsole"
       layout="${message} ${exception:format=toString}"/>
</targets>
<rules>
    <logger name="app" MinLevel="Error" appendTo="consoleError" />
</rules>
</nlog>
  • Current result:
Task Error. System.InvalidCastException: Inner exception
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.<>c.<NestedFunc>b__2_0() in ExceptionRendererTest.cs:line 55
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)    at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.<>c.<NestedFunc>b__2_0() in ExceptionRendererTest.cs:line 55
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
  • Expected result (look at the repeated lines at the bottom of the stack trace):
    This is the result of doing aggregateException.Tostring() in code.
Task Error. System.AggregateException: One or more errors occurred. (Inner exception)
 ---> System.InvalidCastException: Inner exception
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.<>c.<NestedFunc>b__1_0() in ExceptionRendererTest.cs:line 37
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 37
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.NestedFunc(Int32 recursion) in ExceptionRendererTest.cs:line 39
   at DoubleVerify.TP.Service.NetCoreApp.Tests.ExceptionRendererTest.TestAggEx() in ExceptionRendererTest.cs:line 25
  • Are there any workarounds? No, unless writing a custom renderer.
  • Is there a version in which it did work? Not that I know of.
  • Can you help us by writing a unit test?
    Yes. The following code is the unit test I made that generated the "current result" depicted above.
[Test]
public void TestAggEx()
{
    try
    {
        int result = NestedFunc(10);
    }
    catch (Exception e)
    {
        LogManager.LoadConfiguration("nlog.config")
            .GetLogger("app")
            .Error(e, "Task Error.");
    }
}

private int NestedFunc(int recursion)
{
    if(recursion-- == 0)
        return Task<int>.Factory.StartNew(() => throw new InvalidCastException("Inner exception"))
            .Result;
    return NestedFunc(recursion);
}
  • Suggested fix:
    In this code line currentException is the innerException when aggregateException is of type AggregateException. When renderingFormat is "toString" pass aggregateException instead of currentException in the second argument of _ currentRenderFunction._.
    Haven't tested this fix suggestion, just looked at the code a bit, so any other suggestion is welcome.

Thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions