With ASP.NET Core 3.0, the ability to inject an ILogger or ILoggerFactory into the Startup class has been removed, so we can no longer do this:
| public class Startup | |
| { | |
| public Startup(IConfiguration configuration, ILoggerFactory logFactory) | |
| { | |
| Logger = logFactory.CreateLogger<Startup>(); | |
| } | |
| private ILogger Logger { get; } | |
| public void ConfigureServices(IServiceCollection services) | |
| { | |
| Logger.LogInformation("Registering Services"); | |
| // And the rest | |
| } | |
| } |
I understand why this has been done, because creating a temporary DI container just for the startup process adds a lot of complexity and potential for errors.
However, my ConfigureServices method (and the new ConfigureContainer method added in 3.0) does quite a bit of work, including loading extension assemblies; I want to be able to log during that time.
I also want to make sure I only pass around the ILogger from the Microsoft.Extensions.Logging namespace to other objects used at startup.
The Workaround
I use NLog for my logging (https://nlog-project.org/). In my Program’s Main method I configure it like so:
| public static void Main(string[] args) | |
| { | |
| // NLog: setup the nlog config first; this will configure all subsequent nlog factories | |
| // with our nlog config. | |
| NLogBuilder.ConfigureNLog("nlog.config"); | |
| var host = Host.CreateDefaultBuilder(args) | |
| .ConfigureWebHostDefaults(webHostBuilder => | |
| { | |
| webHostBuilder | |
| .UseContentRoot(Directory.GetCurrentDirectory()) | |
| .UseStartup<Startup>(); | |
| }) | |
| .ConfigureLogging(logging => | |
| { | |
| logging.ClearProviders(); | |
| logging.SetMinimumLevel(LogLevel.Trace); | |
| }) | |
| // Use NLog to provide ILogger instances. | |
| .UseNLog() | |
| .Build(); | |
| host.Run(); | |
| } |
You can grab details on how to create an nlog.config file from the NLog docs, I won’t go into it here.
Then, in my Startup class, I can create the NLogLoggerProvider class (this is sort of a factory for creating the Microsoft ILogger instances), and from that I can get my logger instance:
| public class Startup | |
| { | |
| public Startup(IConfiguration configuration) | |
| { | |
| // Get the factory for ILogger instances. | |
| var nlogLoggerProvider = new NLogLoggerProvider(); | |
| // Create an ILogger. | |
| Logger = nlogLoggerProvider.CreateLogger(typeof(Startup).FullName); | |
| } | |
| public void ConfigureServices(IServiceCollection services) | |
| { | |
| Logger.LogInformation("Registering Services"); | |
| // And the rest | |
| } | |
| } |
Hey presto, logging in the Startup class. Note that we are obviously outside the entire DI system for this logging (which is sort of the point).