Using Asp.Net Core 2 Injection for Serilog with Multiple Projects

platypusjh picture platypusjh · Dec 7, 2017 · Viewed 21.3k times · Source

I have Serilog configured for Asp.Net Core 2.0 and it works great via .Net Core dependency injection in my startup web project (if I use it through Microsoft.Extensions.Logging), but I can't access it from any other project.

Here's what I have:

Program.cs

using System;
using System.IO;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Serilog;

namespace ObApp.Web.Mvc
{
    public class Program
    {
        public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
            .Build();

        public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(Configuration)
                .CreateLogger();

            try
            {
                Log.Warning("Starting BuildWebHost");

                BuildWebHost(args).Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .UseSerilog()
                .Build();
    }
}

Startup.cs

Startup .cs is pure default. I haven't altered it in any way from the original template for the new Asp.Net Core MVC project.

I have thought that I might need to add Serilog in the IServiceCollection services, but the article Leaner, meaner ASP.NET Core 2 logging at https://nblumhardt.com/2017/08/use-serilog/ tells me it's unnecessary.

You can then go ahead and delete any other logger configuration that’s hanging around: there’s no need for a "Logging" section in appsettings.json, no AddLogging() anywhere, and no configuration through ILoggerFactory in Startup.cs.

UseSerilog() replaces the built-in ILoggerFactory so that every logger in your application, whether it’s Serilog’s Log class, Serilog’s ILogger, or Microsoft.Extensions.Logging.ILogger, will be backed with the same Serilog implementation, and controlled through the same configuration.

What I Expected

The ability to simply inject a logger via a constructor in any class in any project in the solution. For example:

using Serilog;
public MyTestClass(ILogger logger) { ... }

What I Got - Web (Startup) Project

Injection works in the HomeController if I use the Microsoft.Extensions.Logging wrapper:

using Microsoft.Extensions.Logging;

public class HomeController : Controller
{
    ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
        _logger.LogDebug("Controller instantiated.");
    }
}

Injection fails in any project/class if I try to inject Serilog.ILogger

using Serilog;
using Serilog.Extensions.Logging; // Not sure if this would help.

public class HomeController : Controller
{
    ILogger _logger;

    public HomeController(ILogger logger)
    {
        _logger = logger;
        _logger.Debug("Controller instantiated.");
    }
}

InvalidOperationException: Unable to resolve service for type 'Serilog.ILogger' while attempting to activate 'ObApp2.Web.Mvc.Controllers.HomeController'.

My Bigger Issue - OtherProjects

My bigger issue is that I can't get a logger via DI through any method I've tried if I'm working in another project in the solution.

When I try to inject either using Microsoft.Extensions.Logging or using Serilog I get a missing parameter exception at build time.

using Microsoft.Extensions.Logging;
namespace ObApp.Domain
{
    public class MyTestClass(ILogger<MyTestClass> logger) { ... }

- or -

using Serilog;
namespace ObApp.Domain
{
    public class MyTestClass(ILogger logger) { ... }

Both generate build error similar to:

There is no argument given that corresponds to the required formal parameter 'logger' of 'MyTestClass.MyTestClass(ILogger)'

Questions

  1. When injecting with this type of Serilog configuration, is it recommended that I reference Microsoft.Extensions.Logging or Serilog in the class files where I'm doing the injection?

  2. How can I get DI to work through all projects?

Answer

Rufus Lobo picture Rufus Lobo · Dec 8, 2017

One method that worked for me:

I added an instance of Serilog.Core.Logger using the AddSingleton() method in the ConfigureServices method. This resolved the DI issue. This step replaces the step of assigning a Logger instance to Log.Logger in the StartUp constructor.

services.AddSingleton((ILogger)new LoggerConfiguration()
            .MinimumLevel.Information()
            .WriteTo.File(<...>)
            .CreateLogger());

Also change references in your class files to point to Serilog.Ilogger