Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for context properties #5

Open
isak1080 opened this issue Oct 7, 2024 · 2 comments
Open

Add support for context properties #5

isak1080 opened this issue Oct 7, 2024 · 2 comments

Comments

@isak1080
Copy link

isak1080 commented Oct 7, 2024

In NLog 5 it's possible to add scoped context properties that are automatically attached to all log messages for the lifetime of the scope.

A text-book use case would be in an ASP.Net controller to setup the scope at the beginning of the request, and have all log messages generated within that request to hold some common properties (even if they are not part of the log messages).
This makes it very easy to add things like authenticated username, track "activities" (assign a GUID to each request/operation) etc.

I tested this by cloning this repo and modifying BetterStackLogsTarget.Write() to include the following:

if (IncludeScopeProperties)
{

    var scopeProperties = global::NLog.ScopeContext.GetAllProperties();

    if (scopeProperties != null)
    {
        var scopeDict = new Dictionary<string, object>();

        foreach (var scopeProperty in scopeProperties)
        {
            string key = scopeProperty.Key;
            if (string.IsNullOrEmpty(key)) continue;
            scopeDict[key] = scopeProperty.Value;
        }

        contextDictionary["scope"] = scopeDict;
    }
}

This worked fine in my testing

Note: This would require an update to NLog 5.x (which has been out since 2022)

@PetrHeinz
Copy link
Member

Hi @isak1080!

Thank you for raising this and providing a code snippet! 🙌

I've added resolving this into our roadmap. We'll keep you updated here in GitHub.

Thanks again for the report!

@snakefoot
Copy link
Contributor

snakefoot commented Oct 7, 2024

Notice that you inherit from TargetWithContext, which correctly handles ScopeContext. Where you have access to the following options:

protected override void Write(LogEventInfo logEvent) 
{
   IDictionary<string,object> logProperties = this.GetAllProperties(logEvent);
}

See also: https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target-for-structured-logging

I guess when you have your own properties upfront, then you can do this:

        protected override void Write(LogEventInfo logEvent)
        {
            var contextDictionary = new Dictionary<string, object> {
                ["logger"] = logEvent.LoggerName,
                ["properties"] = logEvent.Properties,
                ["runtime"] = new Dictionary<string, object> {
                    ["class"] = logEvent.CallerClassName,
                    ["member"] = logEvent.CallerMemberName,
                    ["file"] = string.IsNullOrEmpty(logEvent.CallerFilePath) ? null : logEvent.CallerFilePath,
                    ["line"] = string.IsNullOrEmpty(logEvent.CallerFilePath) ? null : logEvent.CallerLineNumber as int?,
                },
            };

            contextDictionary = GetAllProperties(logEvent, contextDictionary);

But if you want all "custom" properties include in ["properties"] then this might be better:

        protected override void Write(LogEventInfo logEvent)
        {
            var properties = GetAllProperties(logEvent); // Assign IncludeEventProperties = true in constructor

            var contextDictionary = new Dictionary<string, object> {
                ["logger"] = logEvent.LoggerName,
                ["properties"] = properties,
                ["runtime"] = new Dictionary<string, object> {
                    ["class"] = logEvent.CallerClassName,
                    ["member"] = logEvent.CallerMemberName,
                    ["file"] = string.IsNullOrEmpty(logEvent.CallerFilePath) ? null : logEvent.CallerFilePath,
                    ["line"] = string.IsNullOrEmpty(logEvent.CallerFilePath) ? null : logEvent.CallerLineNumber as int?,
                },
            };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants