Get ScopeContext properties in AsyncTaskTarget

Viewed 237

I am implementing a custom NLog AsyncTaskTarget, and I need to retrieve values that I have added using Microsoft.Extensions.Logging.ILogger.BeginScope:

using (var scope = logger.BeginScope(new []
{
    new KeyValuePair<string, object>("userId", this.UserId)
}))
{
    logger.Log(..);
}

Rendering this in a layout using ${mdlc:userId} works fine, but I would like to get them directly from MappedDiagnosticsLogicalContext or ScopeContext.

This is what I've tried:

protected override async Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
{
    // returns null
    ScopeContext.TryGetProperty("userId", out var userId);
    userId = MappedDiagnosticsLogicalContext.GetObject("userId");

    // this works fine
    var message = this.Layout.Render(logEvent);
}

How do I get the userId value from the scope?

NLog Version: 5.0.1 (newest)

This GitHub issue is related to this problem, but I found no real solution there.

3 Answers

In your Target-constructor enable the option:

And then you can use GetScopeContextProperties() method:

protected override async Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token)
{
    var contextProperties = GetScopeContextProperties(logEvent);
    var userid = contextProperties["userId"];

}

You can also get a combined dictionary of all properties, by using GetAllProperties() and the do the lookup (Still need to enable IncludeScopeProperties )

Full solution:

using NLog;
using NLog.Targets;

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

    protected override bool SerializeScopeContextProperty(LogEventInfo logEvent, string name, object value, out object serializedValue)
    {
        // return the original (immutable) value
        serializedValue = value;
        return true;
    }

    protected override async Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
    {
        var scopeValues = GetScopeContextProperties(logEvent);
        if (scopeValues is not null)
        {
            scopeValues.TryGetValue("name", out object? scopeValue);
        }
    }
}
Related