Search code examples
c#asp.net-corenlogmicrosoft.extensions.logging

AsyncTaskTarget cannot get scope properties (but TargetWithContext can)


I tried to implement the solution from this SO post: Get ScopeContext properties in AsyncTaskTarget.

However, the solution seems to (no longer?) work. Or, more accurately, it does work with TargetWithContext:

public class ExampleTarget : TargetWithContext
{
    public ExampleTarget()
    {
        this.IncludeScopeProperties = true;
    }

    protected override void Write(LogEventInfo logEvent)
    {
        // works!
        var scopeValues = GetScopeContextProperties(logEvent);
    }
}

but does not with AsyncTaskTarget (even thought that inherits from TargetWithContext):

public class ExampleTarget : AsyncTaskTarget
{
    public ExampleTarget()
    {
        this.IncludeScopeProperties = true;
    }

    protected override async Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
    {
        // returns NULL!
        var scopeValues = GetScopeContextProperties(logEvent);
    }
}

What am I missing?

I am using NLog.Web.AspNetCore 5.2.3 (which uses NLog 5.1.3 and Microsoft.Extensions.Logging 7.0.0).

Scope values are added similar to this:

using (_logger.BeginScope(new[]
{
    KeyValuePair.Create("example-key", "example-value")
}))
{
    _logger.LogInformation("test");
}

Solution

  • Apparently, this was caused because the custom target was still inside an async wrapper via the configuration file:

    <targets>
      <target xsi:type="AsyncWrapper" name="exampleTargetAsync" overflowAction="Block">
          <target xsi:type="ExampleTarget" name="exampleTarget" />
      </target>
    </targets>
    
    <rules>
        <logger name="*" minlevel="Debug" writeTo="exampleTargetAsync" />
    </rules>
    

    Removing the wrapper fixed the issue.

    <targets>
      <target xsi:type="ExampleTarget" name="exampleTarget" />
    </targets>
    
    <rules>
        <logger name="*" minlevel="Debug" writeTo="exampleTarget" />
    </rules>
    

    The wrapper should not be necessary anymore anways, because the target is now already async. However, I was not expecting this to break it.