1

Using ILogger<T>, is there a way to add log properties in addition to the ones covered by the message template?

For example:

_logger.LogInformation("Order {OrderNumber} was registered.", orderNumber);
// Appending extra information to this log entry would be neat:
// OrderOriginSystem = InStoreSupport
// Supervisor = Duncan P.
// StoreLocationCode = ca-north-12

I know Serilog allows various approaches for appending additional properties to log statements, and these seem to play nice with ILogger<T>, e.g.:

using (LogContext.PushProperty("OrderOriginSystem ", orderOriginSystem))
using (LogContext.PushProperty("Supervisor", supervisor))
using (LogContext.PushProperty("StoreLocationCode", slc))
{
    _logger.LogInformation("Order {OrderNumber} was registered.", orderNumber);
}

But I'd like to avoid having to commit to Serilog directly in my code, and hope to instead rely on ILogger<T> throughout.

Is there some trick I've missed?

2 Answers 2

2

ILogger's equivalent of Serilog's PushProperty is BeginScope.

Here is a great article about it

using (logger.BeginScope(new Dictionary<string, object>{
    ["CustomerId"] = 12345,
    ["OrderId"] = 54
}))
{
    logger.LogInformation("Processing credit card payment");
}
Sign up to request clarification or add additional context in comments.

6 Comments

Hey, thanks! I remember seeing this and being put off by the clunky use of Dictionary parameters, but it looks like it's more flexible than I assumed!
Having reviewed the article and played around with BeginScope, it seems to be close enough for my purposes, but... It seems that (unsurprisingly?) the underlying intent of is to add the Scope property to the log entry, and the ability to attach additional properties by using message templates with named placeholder is incidental. What this means is that in order to add, for example, OriginOrderSystem, Supervisor, and StoreLocationCode, as named properties in the log entry, those values will effectively be repeats of the values presented in the Scope property JSON array value. But it's ok.
I am not exactly sure what you mean. It mentions that if object passed to a scope is a dictionary, then each key in it will be added separately, not in Scope array. I guess, that's what you need. Correct me if I am wrong
If you do using(_logger.BeginScope(myScope)) and myScope is a Dictionary object, the Scope property is added to the log statement, and contains a JSON representation of an array with a JSON representation of myScope as an element in that array. The key/values in myScope are not added as individual properties on the log statement. At least, this is what I'm seeing displayed in Seq.
If you do using(_logger.BeginScope("OrderOriginSystem {OrderOriginSystem}", orderOriginSystem) then the Scopeproperty is added to the log statement and contains a JSON representation of an array, with an element reading "OrderOriginSystem InStoreSupport", and you also get on the log statement a property OrderOriginSystem = InStoreSupport. separate from the Scope property.
|
0

Maybe you could try implement customLogger to enrich with property depending on the T:

    public class MyLogger<T> : ILogger<T>
    {
        private readonly Serilog.ILogger _logger;

        public MyLogger()
        {
            var loggerConfiguration = new LoggerConfiguration();
            //check the type of T
            if (typeof(T) == typeof(WeatherForecastController))
            {
                loggerConfiguration
                    .WriteTo.Console()
                    .Enrich.WithProperty("OrderOriginSystem ", "InStoreSupport");
            }
            _logger = loggerConfiguration.CreateLogger();
        }

        public IDisposable? BeginScope<TState>(TState state) where TState : notnull
        {
            throw new NotImplementedException();
        }

        public bool IsEnabled(LogLevel logLevel) => _logger.IsEnabled((LogEventLevel)logLevel);

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (!IsEnabled(logLevel))
            {
                return;
            }

            _logger.Write((LogEventLevel)logLevel, exception, formatter(state, exception));
        }
    }

Then in program.cs (will not need configure serilog in program.cs)

builder.Services.AddSingleton(typeof(ILogger<>), typeof(MyLogger<>));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.