Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How use Microsoft.Extensions.Logging from Library code?

Tags:

c#

logging

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.

  • Where do I get an ILoggerFactory? I just create a global one for my library?
  • How the client will configure the factory to add the ILoggerProvider he want?
  • Will the ILoggerFactory be auto configured with the correct providers if the client has done its configuration correctly?

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?

  • What if the client library doesn't has a Generic Host or don't use dependency injection?

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

like image 806
Maxime Bourque-Fortin Avatar asked Dec 13 '19 21:12

Maxime Bourque-Fortin


1 Answers

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.

tl;dr

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:

  • Dependency injection in .NET
  • Logging in .NET
like image 149
Clint Avatar answered Oct 28 '22 21:10

Clint