Factory Provider Repository Builders
Yeah, naming is hard.
Microsoft.Extensions.Logging, there are two types in particular that I kept conflating:
ILoggerFactory. Even though they both can create instances of
ILogger, they are actually completely different!
In this post, I’m going to cover the main types of
Microsoft.Extensions.Logging and describe their intended use.
Like all other logging frameworks,
Microsoft.Extensions.Logging defines a sequence of levels for its logs. In increasing order of severity, they are
Critical. The meanings of these values are well documented, along with advice on when to use each.
There is another “log level” which is not really a log level:
None. This is technically part of the enumeration, but is used during configuration to indicate that no logs for that part of the system should be logged. The
None value is not used during logging.
ILogger is a logger that your code can use to write log messages to. There are three core methods:
IsEnabled tests whether a log level is enabled on that logger;
Log is the core logging method that is used to write log messages; and
BeginScope defines a logging scope.
We’ll cover logging scopes later in this series. That leaves
Log, which are the core logging methods. There’s a bunch of logging extension methods that build on that core; the common methods like
LogInformation are just wrappers around
Log, with the appropriate arguments.
ILogger has a “name” (also called a “category”). The idea is that each
ILogger instance is used by a different component of the application.
ILogger is a base interface that provides the core logging functionality, but it is seldom used directly. There are some exceptions (e.g., Azure Functions will pass you an
ILogger), but most of the time your code will log to an
ILogger<T> is a logger that is named after a type
T. All logs sent to an
ILogger<T> (with the default implementation) will have a logger name/category of
ILogger<T> is derived from
ILogger and adds no new functionality.
If you’re using dependency injection, an instance of
ILogger<T> is usually injected into your type
T. So, each time you have a constructor that takes an
ILogger<T>, you are defining a “component” for your application.
Personally, I’m not a huge fan of this style of getting a logger, but it works. In my applications, the concept of a “component” seldomly has a 1:1 relationship with “types that log”. It tends to work out best for ASP.NET Controllers, but less so for utility types used by services (where I usually want the utility type to use the service’s component name). Of course, you can just pass an
ILogger) to the utility type, and that’s the way this is generally resolved.
So, here’s a question that you may have: if
ILogger<T> provides no benefit over
ILogger (other than being named after a type
T), why does this type exist at all? The answer is logging extension methods, which we’ll look at in more detail further in this series.
The logger provider is a type that (drum roll…) provides
ILogger instances. But not just that; it provides
ILogger instances for a specific logging system. Microsoft publishes a few logger providers that support writing to debugger output, the Console, the Windows Event Log, Event Tracing for Windows (ETW), and others.
There are plenty of third-party logging providers, too. The primary purpose of a logging provider is to take log events and forward them to some logging backend. So there are logging providers for all kinds of logging backends: Serilog, Seq, log4net, etc. This allows you to write code that is independent of a logging framework (logging to an
ILogger<T>), and the implementation at runtime hits a specific backend (or multiple ones!).
You can also create your own implementations of
ILoggerProvider. In my DotNetApis project, I have one provider that stores logs in memory so they can be returned to the frontend as part of the HTTP response, another provider that streams JSON logs to a GZIP-compressed Azure blob, and several others.
Creating reusable implementations of
ILoggerProvider is perhaps the most underdocumented part of
Microsoft.Extensions.Logging. The providers in my DotNetApis project at this point are incomplete; there is no way I would put them in a NuGet package or anything. A proper, reusable
ILoggerProvider is more involved; later in this series I’ll look specifically at implementing
ILoggerProvider properly, and cover all the necessary details.
ILoggerProvider is a way to extend
Microsoft.Extensions.Logging by implementation. However, you don’t ever want to consume a logger provider directly. Even though
ILogger instances, you never actually want to call that method to get a logger. To get loggers, you want to use dependency injection or
ILoggerFactory is the mastermind that brings together all the types above. Conceptually, an
ILoggerFactory has a collection of
ILoggerProviders, and the
ILogger<T> instances for the application.
Registering ILoggerProviders with ILoggerFactory
This is where the official documentation starts to fall short. In the ASP.NET Core world, ASP.NET Core itself takes care of creating an
ILoggerFactory instance, which it then passes to your application to configure. Your application can then call
AddProvider or a higher-level provider-specific method such as
Fortunately, even without ASP.NET Core, it’s not too difficult to do this yourself using the
LoggerFactory type in
Getting ILogger<T> Instances from ILoggerFactory
In the ASP.NET Core world, the
ILoggerFactory is included in your Dependency Injection container, and it already knows how to get
ILogger<T> values out of it, and everything is magical rainbows.
When you’re outside of the ASP.NET Core world, you can still use
ILoggerFactory in this way. You just have to:
- Provide the
ILoggerFactoryinstance to your DI container.
- Configure your DI container to resolve
ILogger<T>instances by calling
The exact instructions on how to do this depends on your DI container of choice.
Of course, there’s another option, too. You can just provide the
ILoggerFactory instance, and your consuming types can take the
ILoggerFactory and create their own
What Are the ILoggerFactory’s ILoggers?
Before closing out this post, I just want to point out that the
ILogger instances provided by
ILoggerFactory are not the same as the
ILogger instances provided by
ILogger is a logger that logs to that specific provider. An
ILogger is a logger that logs to all registered providers.
In other words, the
ILogger<T> loggers are composite loggers; they forward log messages to each provider’s
This post has described the various logging types from the component’s perspective (
ILogger) and working out towards the application’s perspective (
ILoggerFactory). Let’s briefly review, going the other way this time.
ILoggerFactory is a collection of
ILoggerProviders that creates composite
ILoggerProvider ia a provider for a specific logging system. It provides
ILogger loggers to the
Each component gets an
ILogger) from the
ILoggerFactory that it should use for logging.