1

I read some asp.net core source code and get confused about injecting a Logger, let's take a middleware for example:

public class CookiePolicyMiddleware {
   private readonly RequestDelegate _next;
   private readonly ILogger _logger;

   public CookiePolicyMiddleware(RequestDelegate next, IOptions<CookiePolicyOptions> options, ILoggerFactory factory) {
      Options = options.Value;
      _next = next;
      _logger = factory.CreateLogger<CookiePolicyMiddleware>();
   }
   // ...
}

so why not we just inject ILogger directly as:

public class CookiePolicyMiddleware {
   private readonly RequestDelegate _next;
   private readonly ILogger<CookiePolicyMiddleware> _logger;

   public CookiePolicyMiddleware(RequestDelegate next, IOptions<CookiePolicyOptions> options, ILogger<CookiePolicyMiddleware> logger) {
      Options = options.Value;
      _next = next;
      _logger = logger;
   }
   // ...
}

because the default CreateDefaultBuilder method in Program.cs will call services.AddLogging() which will registe ILogger<> as Logger<> (https://source.dot.net/#Microsoft.Extensions.Logging/LoggingServiceCollectionExtensions.cs,42)

Isn't the second approach more straightforward?

  • Does this answer your question? [Should I take ILogger, ILogger, ILoggerFactory or ILoggerProvider for a library?](https://stackoverflow.com/questions/51345161/should-i-take-ilogger-iloggert-iloggerfactory-or-iloggerprovider-for-a-libra) – galdin Feb 03 '22 at 13:42
  • 1
    What if you want multiple logger instances or a more descriptive category name. It also depends how it is registered. As a singleton, the injected logger instance will be shared over multiple instances. It might be usefull or not. – Jeroen van Langen Feb 03 '22 at 14:36

1 Answers1

2

Well, not sure if your example is a copy & paste mistake and if you were meant to inject it a middleware's .Invoke.

In constructor injection, they act same and ILogger<T> should be preferred unless you are manually instantiating types and want pass a specific logger to it.

Inside .Invoke, the case is different, because the middleware runs as singleton.

If you inject ILogger<T> in the middle ware constructor, it will create a logger with the log level at the point when it was first created.

If you change the log in appsettings.json while the application runs, it won't use the new values in the middleware logger and still log according to the old settings until the application is restarted.

This is because Logger<T> is only a wrapper around ILoggerFactory.Create to easily inject an typed logger w/o going over to the factory. The actual logger class is Logger. When the logger is created, it gets a copy of the filter options (see source).

So the changes in filter settings will only be applied on newly created loggers.

What happens in the code above is, that the factory is injected and the logger created in the Invoke method, which creates a new logger on every request, thus also loading the most recent filter and settings from appsettings.json and allows live changing of log level without restarting of the application.

Tseng
  • 61,549
  • 15
  • 193
  • 205
  • Thank you for your great answer, really helped me a lot. –  Feb 04 '22 at 04:40
  • just a small question, https://source.dot.net/#Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs,27 you can see that this middlware doesn't call CreateLogger in Invoke method, still create a logger in the constructor –  Feb 04 '22 at 09:45
  • 1
    Nothing wrong with it, since most applications do not expect the log filters to change while the application runs. If one has a specific requirement that log levels must be able to be changed without restarting the application, this scenario won't work – Tseng Feb 04 '22 at 10:13
  • thanks for your comment. So in that case, why CookiePolicyMiddleware doesn't inject `ILogger` directly into its constructor if there is no requirement that log levels must be able to be changed without restarting the application? –  Feb 04 '22 at 22:53