Using Asp.Net Core 2 Injection for Serilog with Multiple Projects
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
This solution correctly injects the logger into classes in an external project or library.
I also tried the answer suggested by Rufus. I didn't have the problem described by the OP, but I had an even weirder problem: after ILogger
was injected into the external project, logging to MS SQL stopped working. Console logging worked. Since Serilog is mainly a static service, I realized I should treat Log.Logger
as the factory. The benefit is that you can still customize ILogger
references (for example, adding ForContext
in the constructor) without affecting the "singleton" service.
I've posted a full working solution on github including a console program that sets up DI (would work the same from ASP.NET Core), an external library that contains the Serilog getting-started divide-by-zero demo, and another library that manages Serilog as a service.
The important part, Serilog service registration, looks like this (and as a bonus, we tie to ProcessExit
for transparent cleanup):
using Microsoft.Extensions.DependencyInjection;using System;namespace Serilog.Injection{ public static class RegisterSerilogServices { public static IServiceCollection AddSerilogServices(this IServiceCollection services) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.Console() .WriteTo.MSSqlServer(@"xxxxxxxxxxxxx", "Logs") .CreateLogger(); AppDomain.CurrentDomain.ProcessExit += (s, e) => Log.CloseAndFlush(); return services.AddSingleton(Log.Logger); } }}
I have actually been struggling with the very same setup, but I managed to get it to work. The solution is only slightly different from your code examples in these ways:
- It depends on the
Microsoft.Extensions.Logging.ILogger
. - The Logger is injected using
ILogger<Type>
Your controller code already fit these requirements, which is why that did work.
using Microsoft.Extensions.Logging;public class HomeController : Controller{ ILogger _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; _logger.LogInformation("Controller instantiated"); }}
For other classes in other projects just make sure you depend on the default ILogger interface, not Serilog's implementation. This also has the benefit that, if you want to replace Serilog later, you can do this without having to remove all references to it throughout your other projects as well.
using Microsoft.Extensions.Logging;public class MyBusinessObject{ ILogger _logger; public MyBusinessObject(ILogger<MyBusinessObject> logger) { _logger = logger; } public void DoBusinessLog() { _logger.Information("Executing Business Logic"); }}
For some reason DI only manages to instantiate a Logger instance when requiring an ILogger<Type>
but not for ILogger
. Why that is exactly I don't know, maybe someone else can answer that one.