Reputation: 3467
I am (as something of a novice) implementing my own custom logger for use in ASP.NET Core MVC apps. I have this logger working functionally in every regard. But I cheated a little so far, namely I implemented the ILogger.IsEnabled
method as follows:
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
Functionally, this works fine, since the framework ensures that the Log()
method is only invoked if the log level is at or higher than the one specified. So the correct "things" are being logged and the lower-level "things" are not being logged as expected.
However, I also want to support the following kind of situation in my code, where _logger
is typed as ILogger
and is properly injected in my controller:
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("This is an expensive message to generate: " +
JsonConvert.SerializeObject(request));
}
To make this effective, my IsEnabled()
method should be able to know what the log level IS for the instance of the logger that was created with my LoggerProvider
, but I don't know how to get that information directly, or how to pass it properly to the injected instance of the the logger I am working with.
Complex examples and tutorials I have been able to find seem to be constructed in every case for console app types, not network app types, and so far I have been unsuccessful at figuring out how to do this through the templated Startup
class in ASP.NET MVC.
What is the simplest and most effective way to stop cheating at my custom IsEnabled()
method in order to avoid the unnecessary serialization (in my example) if none of the registered loggers in the injected instance are handling the Debug
log level? Or do you have a favorite example or tutorial in the ASP.NET core setting you can point me to?
Upvotes: 5
Views: 3282
Reputation: 1981
You don't need to implement filtering logic in your logger's IsEnabled
method because this logic is implemented in the wrapper of each registered logger. The wrapper type is Microsoft.Extensions.Logging.MessageLogger
, and it also contains the IsEnabled
method. Here's a piece of code from the Microsoft.Extensions.Logging.Logger.Log
method:
for (int i = 0; i < loggers.Length; i++)
{
ref readonly MessageLogger loggerInfo = ref loggers[i];
if (!loggerInfo.IsEnabled(logLevel))
{
continue;
}
LoggerLog(logLevel, eventId, loggerInfo.Logger, exception, formatter, ref exceptions, state);
}
Upvotes: 0
Reputation: 566
You can take a look at built-in loggers source code and see how they implement it.
In short, they only check that logLevel != LogLevel.None
, but depending on the logger logic, you might also want to check some other configuration. For example, DebugLogger
logger also checks the Debugger.IsAttached
property and EventLogLogger
checks the EventLogSettings.Filter
(supplied via constructor).
Update
To make this effective, my
IsEnabled()
method should be able to know what the log level IS for the instance of the logger that was created with myLoggerProvider
, but I don't know how to get that information directly, or how to pass it properly to the injected instance of the the logger I am working with.
You can create an implementation of ILoggerProvider
which in turn can make use of dependency injection to get the configuration you want. If you want to use the options pattern to configure it, you must do something along the lines of:
public class MyLoggerProvider : ILoggerProvider
{
private readonly IOptions<MyLoggerOptions> _options;
public MyLoggerProvider(IOptions<MyLoggerOptions> options)
{
_options = options;
}
public ILogger CreateLogger(string name)
{
return new MyLogger(name, _options.Value);
}
}
And optionally add an extension method to make registration easier:
public static class MyLoggerExtensions
{
public static ILoggingBuilder AddMyLogger(this ILoggingBuilder builder, Action<MyLoggerOptions> configure)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, MyLoggerProvider>());
LoggerProviderOptions.RegisterProviderOptions<MyLoggerOptions, MyLoggerProvider>(builder.Services);
builder.Services.Configure(configure);
}
}
Upvotes: 2