Search code examples
c#logging.net-coredependency-injection.net-core-logging

Dependency Injecting Logging in Non-Hosting .NET Console


How can I implement ILogger dependency injection (like a hosted app) in a non-hosted Console app? In the code below, I'd like to work like logTest1. Is that possible?

Below is my appsettings.json - I would prefer to not have to define every class, but this doesn't work anyway.

{
    "Logging": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information",
        "ClientTestCli.LogTest":  "Trace"
      }
}

My Console app Program:

class Program
{
    static void Main(string[] args)
    {
        HandleArgs(args);

        var serviceProvider = ContainerConfiguration.Configure();

        var logTest1 = new LogTest();
        var logTest2 = new LogTest(serviceProvider.GetService<ILogger<LogTest>>());
        logTest1.LogStuff();
        logTest2.LogStuff();
    }
}

Container configuration:

internal static class ContainerConfiguration
{
    public static ServiceProvider Configure()
    {
        return new ServiceCollection()
            .AddLogging(l => l.AddConsole())
            .Configure<LoggerFilterOptions>(c => c.MinLevel = LogLevel.Trace)
            .BuildServiceProvider();
    }
}

A test class:

internal class LogTest
{
    ILogger<LogTest> logger;
    public LogTest(ILogger<LogTest> logger = null)
    {
        this.logger = logger;
    }

    public void LogStuff()
    {
        logger?.LogCritical("This is a critical log");
    }
}

Solution

  • Add LogTest to the service collection and resolve it using the provider and the provider will inject the configured loggers

    Refactor your composition root

    internal static class ContainerConfiguration {
        public static IServiceProvider Configure(Action<IServiceCollection> configuration = null) {
            var services = new ServiceCollection()
                .AddLogging(logging => {
                     logging.ClearProviders();
                     logging.AddConsole();
                })
                .Configure<LoggerFilterOptions>(c => c.MinLevel = LogLevel.Trace);
    
            configuration?.Invoke(services);
    
            return services.BuildServiceProvider();
        }
    }
    

    And use that at start up

    static void Main(string[] args) {
        HandleArgs(args);
    
        IServiceProvider serviceProvider = ContainerConfiguration.Configure(services => {
            services.AddTransient<LogTest>();
        });
    
        LogTest logTest = serviceProvider.GetService<LogTest>();
    
        logTest.LogStuff();
    }
    

    If the provider is used, it will take care of building object graph, which includes injecting required dependencies.

    .Net Fiddle of a working example.