1
votes

New to .NET Core here. I've searched for another documentation, thread or guide that answers my questions but can't find one, if you think one does, please point it out.

I'm trying to create a simple .NET 5 console app with DI and literally getting stuck on structuring classes with logging.

  1. Is this the correct way to pass in a logger (or any other service) to a sub class using DI in .NET Core? As per below code, in my parent class constructor I take an extra ILogger for each sub class eg. ILogger<SubClass>?

    public TestClass(ILogger<TestClass> log, ILogger<SubClass> subClassLog, IConfiguration config)
    
  2. How would I initialize a logger in my static proc StaticProc?

    public static async Task<bool> StaticProc()
    

Program.cs:


    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using System;
    using System.IO;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static async Task Main(string[] args)
            {
                var builder = new ConfigurationBuilder();
                BuildConfig(builder);
    
                var host = Host.CreateDefaultBuilder()
                    .ConfigureServices((context, services) =>
                    {
                        services.AddTransient<ITestClass, TestClass>();
                        services.AddTransient<ISubClass, SubClass>();
                    })
                    .ConfigureLogging(logBuilder =>
                    {
                        logBuilder.SetMinimumLevel(LogLevel.Trace);
                        logBuilder.AddLog4Net("log4net.config");
                    })
                    .Build();
    
                var log = host.Services.GetService<ILoggerFactory>().CreateLogger<Program>();
    
                log.LogInformation($"Application Started");
    
                var svc = ActivatorUtilities.CreateInstance<TestClass>(host.Services);
                await svc.Run();
    
                log.LogInformation($"Application Ended");
            }
    
            static void BuildConfig(IConfigurationBuilder builder)
            {
                builder.SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"}.json", optional: true)
                    .AddEnvironmentVariables();
            }
        }
    }

TestClass.cs:


    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        public class TestClass : ITestClass
        {
            private readonly ILogger<TestClass> _log;
            private readonly ILogger<SubClass> _subClassLog;
            private readonly IConfiguration _config;
    
            public TestClass(ILogger<TestClass> log, ILogger<SubClass> subClassLog, IConfiguration config)
            {
                _log = log;
                _subClassLog = subClassLog;
                _config = config;
            }
    
            public async Task Run()
            {
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogDebug("Loop debug {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogInformation("Loop info {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogWarning("Loop warn {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogError("Loop error {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogCritical("Loop critical {loopNumber}", i);
    
                var subClass = new SubClass(_subClassLog, _config);
                await subClass.AnotherProc();
    
                await SubClass.StaticProc();
            }
        }
    }

SubClass.cs:


    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        public class SubClass : ISubClass
        {
            private readonly ILogger<SubClass> _log;
            private readonly IConfiguration _config;
    
            public SubClass(ILogger<SubClass> log, IConfiguration config)
            {
                _log = log;
                _config = config;
            }
    
            public async Task AnotherProc()
            {
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogDebug("Loop debug {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogInformation("Loop info {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogWarning("Loop warn {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogError("Loop error {loopNumber}", i);
    
                for (int i = 0; i < _config.GetValue<int>("Loop"); i++)
                    _log.LogCritical("Loop critical {loopNumber}", i);
            }
    
            public static async Task<bool> StaticProc()
            {
                var returnBool = true;
    
                try
                {
                    throw new Exception("");
                }
                catch (Exception ex)
                {
                    returnBool = false;
    
                    // No instance, so no _log exception.
                    // How to create standalone ILogger?
                }
    
                return returnBool;
            }
        }
    }

appsettings.json:


    {
      "Loop": 15
    }

log4net.config:


    <?xml version="1.0" encoding="utf-8"?>
    <log4net>
        <appender name="Info" type="log4net.Appender.RollingFileAppender">
            <threshold value="DEBUG" />
            <param name="File" value="App_Data\\Log\\Info.log"/>
            <param name="AppendToFile" value="true"/>
            <maximumFileSize value="5120KB"/>
            <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
            <maxSizeRollBackups value="1000"/>
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%d{yyyy-MM-dd HH:mm:ss} - [%t] %-5p %c %x - %m%n" />
            </layout>
        </appender>
        <appender name="Error" type="log4net.Appender.RollingFileAppender">
            <threshold value="Error" />
            <param name="File" value="App_Data\\Log\\Error.log"/>
            <param name="AppendToFile" value="true"/>
            <maximumFileSize value="5120KB"/>
            <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
            <maxSizeRollBackups value="1000"/>
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%d{yyyy-MM-dd HH:mm:ss} - [%t] %-5p %c %x - %m%n" />
            </layout>
        </appender>
        <root>
            <appender-ref ref="Info" />
            <appender-ref ref="Error" />
        </root>
    </log4net>

1
have you checked this article, it describes how we can use a DI Container in console app pradeeploganathan.com/dotnet/…Vilsad P P
I had already looked at a couple of articles on pradeeploganathan.com, and also used youtube.com/watch?v=GAOCe-2nXqc. Neither gave me exactly what I needed. The LoggerFactory is what I was after. Thank you both for your help.martin

1 Answers

0
votes

Summary

You can also inject a ILoggerFactory instead of the ILogger:

public TestClass(ILoggerFactory loggerFactory, IConfiguration config)
{
     // create a class logger
     _log = loggerFactory.CreateLogger<TestClass>();

     // and whenever you need a new instance of a special class logger use this:
     _subClassLog = loggerFactory.Create<SubTestClass>();
     _config = config;
}

But be aware that this creates a new instance of the ILogger. Logging in asp net core In your case if you really need the static method with a logger create it only if it already exists:

private static readonly ILogger<SubClass> _log;
private readonly IConfiguration _config;

public SubClass(ILoggerFactory loggerFactory, IConfiguration config)
{
     _log = _log ??= loggerFactory.CreateLogger<SubClass>();
     _config = config;
}

But I will prefere or better advice you to do it without having statics and just register the service as singleton.

Example 1

I added a full example here: dotnet fiddle Example 1 Also with the working DI for console apps like mentioned in the comments.

Example 2

Imo you should use it not with static methods. Just look to my second example here dotnet fiddle Example 2