Search code examples
c#logging.net-coredependency-injectionserilog

.Net Core Worker Service Injecting logging into .Net Standard Library Class


I'm trying to inject my Serilog Logger into a .Net Standard 2.0 library class from a .NET Core Worker Service Project. However whenever the SetManager class is created, it is receiving no logger. (The default is set to null) Therefore I get a value is null error whenever I try use the logger.

Can anyone help? Host Builder Below

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .ConfigureServices((hostContext, services) =>
    {
        IConfiguration configuration = hostContext.Configuration;
        ConfigDto configDto = configuration.GetSection("AppSettings").Get<ConfigDto>();
        services.AddSingleton(configDto);
        services.AddHostedService<Service>();
    })
    .UseSerilog();
}

Class in Worker Project that calls SetManager which is the Class in the .NET Standard Library.

public Service(ConfigDto config, ILogger<Service> logger = null)
{
    _logger = logger;
    _logger.LogTrace("Service object created");

    try
    {
        _logger.LogTrace("Getting config");
        _config = config;
    }
    catch (Exception ex)
    {
        _logger.LogCritical("Unable to load configuration. Stopping service:\n{0}", ex.Message);
        Stop();
    }
    _setManager = new SetManager(_config);
    _setManager.ReadyToProcess += SetManager_ReadyToProcess;
}

SetManager Constructor

public SetManager(ConfigDto config, ILogger<SetManager> logger = null)
{
    _logger = logger;
    _config = config;
    _productList = GetProducts();

    _timer.Interval = 3000;
    _timer.Elapsed += Timer_Elapsed;
}

Packages

<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" /> 
<PackageReference Include="Serilog.Extensions.Hosting" Version="3.0.0" /> 
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> 
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> ` 

The Service class gets the logger injected fine. Its the SetManager class that doesn't receive the logger.

Serilog Logger setup in Main method.

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Verbose() 
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) 
    .Enrich.With(new ThreadIdEnricher()) 
    .Enrich.FromLogContext() 
    .WriteTo.Console(LogEventLevel.Verbose, loggerTemplate, theme: AnsiConsoleTheme.Literate) 
    .WriteTo.File(logfile, LogEventLevel.Verbose, loggerTemplate, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 90) 
    .CreateLogger();

Solution

  • The Service class gets the logger injected fine. Its the Set Manager class that doesn't receive the logger.

    You are manually creating the SetManager

    _setManager = new SetManager(_config);
    

    using the constructor with the default (null) logger.

    I would suggest explicitly injecting the manager into the service worker

    public Service(SetManager manager, ILogger<Service> logger)
    {
        _logger = logger;
        _logger.LogTrace("Service object created");
    
        try
        {
            _logger.LogTrace("configuring manager");            
            _setManager = manager;
            _setManager.ReadyToProcess += SetManager_ReadyToProcess;
        }
        catch (Exception ex)
        {
            _logger.LogCritical("Unable to load configuration. Stopping service:\n{0}", ex.Message);
            Stop();
        }
    }
    

    Making sure that the manager is also registered with the service collection with an appropriate lifetime scope

    This will allow the correct Logger to be injected into the manager when the manager is injected into the service and follows the Explicit Dependencies Principle