I have a hard time understanding what is the best way to use Microsoft.Extensions.Logging in a general library.
with NLog you can do:
public class MyClass
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public void foo()
{
Logger.Info("foo");
}
}
And you're done! You let the client configure its target but that not your problem anymore.
But with Microsoft.Extensions.Logging the code look more like:
public class MyClass
{
private readonly Ilogger Logger;
public MyClass(ILogger<MyClass> logger)
{
Logger = logger;
}
public void foo()
{
Logger.LogInformation("foo");
}
}
So now I need to pass a ILogger at the ctor.
OK no problem I can get one from an ILoggerFactory.
Now I need an ILoggerFactory.
That sound wrong since to create a LoggerFactory in the library code you need reference to Microsoft.Extensions.Logging while you should only reference Microsoft.Extensions.Logging.Abstractions. It's a clue of a code smell.
The dependency injection in the constructor look fine when you're in a ASP.Net Core app or a service but want about a general library that could be used in a console app or a WinForm/WPF application?
So now the client now need to pass an ILogger?
//Client code
var myClass = new MyClass(); //with NLog
var myClass = new MyClass(loggerFactory.CreateLogger<MyClass>());
First the code is horrible and now the client must keep track of a ILoggerFactory somewhere.
Do I miss something or Microsoft.Extensions.Logging look terrible for general library? Do you have code example of project of this type using Microsoft.Extensions.Logging?
Thank you
Sounds like you've run into a common issue library developers hit upon.
Ultimately in environments where dependency injection is considered the default (see .NET core etc), it can be tricky to know how this should be handled.
I actually asked a similar question a while back:
Writing a library that is dependency-injection "enabled"
I even wrote a utility library (https://github.com/ckpearson/IoCBridge) that was ill-conceived and designed to help allow the DI container to be abstracted from the perspective of the library.
In your case here however, I can see why NLog would seem more elegant compared to the .NET logging approach, but I'm here to say that's a false comfort.
Constructor parameters in the way the .NET logging abstractions use adheres to the Explicit Dependencies Principle, whereas the NLog solution completely hides the dependency, let alone allowing you to replace it.
I agree, that without an environment that supports dependency injection, it becomes a little cumbersome, but that's usually a good sign that dependency injection is something you might want to add.
So, yes, your library classes should take the logger as a parameter, and callers should either resolve your type via a dependency container, or should have to provide the logger instance explicitly.
You don't (and shouldn't) create a logger factory in your library, instead the application consuming your library should be responsible for this; if they're using a .NET environment that supports the host builder and dependency injection, the platform will take care of this for them.
I would recommend brushing up on the Microsoft documentation:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With