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

Function Enricher #29

Open
jmiddour opened this issue Jul 18, 2018 · 2 comments
Open

Function Enricher #29

jmiddour opened this issue Jul 18, 2018 · 2 comments

Comments

@jmiddour
Copy link

jmiddour commented Jul 18, 2018

First off, apologies if this is in the wrong location as it didn't exactly fit any of the repos

I had a need where I wanted to enrich a log event with the current value of a property at the time it was logged. Specifically, we have desktop applications that are able to change Environment dynamically, so I wanted a way to reflect that in log messages -- and route log events to the correct sinks. I know ForContext<> already does this to some degree, but I didn't want to have to worry about popping and re-pushing when the property value changes.

So, took the existing property enricher and changed it to enrich with the result of the function. I am sharing below to see if anyone else might be interested or will know if it runs counter to a specific design choice. Also, if anyone has a better way of doing it, I'd be interested in that as well.

The code:

public class FunctionEnricher : ILogEventEnricher
    {
        readonly string _name;
        readonly Func<object> _value;
        readonly bool _destructureObjects;

        /// <summary>
        /// Create a new Function enricher.
        /// </summary>
        /// <param name="name">The name of the property.</param>
        /// <param name="value">Function to evaluate.</param>
        /// <returns>A handle to later remove the property from the context.</returns>
        /// <param name="destructureObjects">If true, and the value is a non-primitive, non-array type,
        /// then the value will be converted to a structure; otherwise, unknown types will
        /// be converted to scalars, which are generally stored as strings.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public FunctionEnricher(string name, Func<object> value, bool destructureObjects = false)
        {
            if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Property name must not be null or empty.", nameof(name));
            _name = name;
            _value = value;
            _destructureObjects = destructureObjects;
        }

        /// <summary>
        /// Enrich the log event.
        /// </summary>
        /// <param name="logEvent">The log event to enrich.</param>
        /// <param name="propertyFactory">Factory for creating new properties to add to the event.</param>
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
            if (propertyFactory == null) throw new ArgumentNullException(nameof(propertyFactory));
            var property = propertyFactory.CreateProperty(_name, _value.Invoke(), _destructureObjects);
            logEvent.AddPropertyIfAbsent(property);
        }
    }

And an example of it's usage:

static void Main(string[] args)
        {
            string env = "test";

            var log = GetLoggerConfiguration(()=>env)
                        .WriteTo.Logger(lc=> lc.Filter.ByExcluding(Matching.WithProperty("Environment","prod"))
                                               .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Environment} {Message:lj}{NewLine}"))
                        .WriteTo.Logger(lc => lc.Filter.ByIncludingOnly(Matching.WithProperty("Environment", "prod"))
                                               .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] prodonly {Message:lj}{NewLine}"))
                        .WriteTo.Console()
                        .CreateLogger();

            log.Error("This is a test log message");

            env = "prod";

            log.Error("This is another log message");

            Console.ReadLine();

        }

  public static LoggerConfiguration GetLoggerConfiguration(Func<object> Environment)
        {

            var _loggerConfig = new LoggerConfiguration()
                            .Enrich.FromLogContext()
                            .Enrich.WithProperty("ApplicationInstanceId", ApplicationInstanceId)
                            .Enrich.With(new FunctionValueEnricher("Environment",()=>Environment));
                            
            return _loggerConfig;
        }

And the output:
[15:22:23 ERR] test This is a test log message
[15:22:23 ERR] This is a test log message
[15:22:24 ERR] prodonly This is another log message
[15:22:24 ERR] This is another log message

@alexdresko
Copy link

I love this. Just used it and it worked great! Could you create a nuget package like https://www.nuget.org/packages/Serilog.Enrichers.Process/?

@jmiddour
Copy link
Author

jmiddour commented Dec 5, 2019

I can definitely create a nuget package for it, but it wouldn't be in the Serilog namespace as I'm not part of their organization.

I'm tied up with a project for the next week or so, but I will see what I can get done after that.

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

2 participants