简体   繁体   中英

How to configure logging provider with dependency injection for .NET library

I have a typical scenario where I'm developing a .NET Core library and want the calling application to be able to optionally configure the logging options.

Ideally I'd like to use dependency injection on the ILogger property of each class in the library. What I can't work out is how to have the DI container is initialised in one assembly, the application executable, and consume it in a different one, the library assembly.

The best I can come up with is the example below where the DI container is initialised in the executing assembly and then stored in an intermediary assembly that the library then accesses. My question is whether there's a way to do without the intermediary assembly AND avoid the need to pollute the library constructors with a logging instance which may or may not be needed?

In the contrived example below each namespace represents a separate assembly. The library assembly does not know anything about the Application assembly.

using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;

namespace MainAssembly
{
    class Program
    {
        public static Microsoft.Extensions.Logging.ILogger logger = NullLogger.Instance;

        static void Main()
        {
            Console.WriteLine("DI + Log Test");

            // Set up logging factory.
            var loggerFactory = new LoggerFactory();
            var serilogConfig = new LoggerConfiguration()
                .Enrich.FromLogContext()
                .MinimumLevel.Is(Serilog.Events.LogEventLevel.Debug)
                .WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level}] ({SourceContext}.{Method}) {Message}{NewLine}{Exception}")
                .CreateLogger();
            loggerFactory.AddSerilog(serilogConfig);

            // Set up dependency injection services.
            ServiceCollection serviceCollection = new ServiceCollection();
            serviceCollection.AddSingleton<LoggerFactory>(loggerFactory);
            IntermediaryAssembly.DI.ServicesSingleton = serviceCollection.BuildServiceProvider();

            // Attempt to get a logger from the DI container.
            logger = IntermediaryAssembly.DI.TryGetLogger<Program>();

            logger.LogDebug("Logging initialised.");

            var lib = new LibraryAssembly.DoIt();
            lib.DoWork();

            Console.WriteLine("Finished.");
        }
    }
}

namespace IntermediaryAssembly
{
    public class DI
    {
        public static ServiceProvider ServicesSingleton;

        public static ILogger<T> TryGetLogger<T>()
        {
            if (ServicesSingleton != null && ServicesSingleton.GetServices<LoggerFactory>().Any())
            {
                return ServicesSingleton.GetRequiredService<LoggerFactory>().CreateLogger<T>();
            }
            else
            {
                return NullLogger<T>.Instance;
            }
        }
    }
}

namespace LibraryAssembly
{
    public class DoIt
    {
        public Microsoft.Extensions.Logging.ILogger logger = NullLogger.Instance;

        public DoIt()
        {
            // Attempt to get a logger from the DI container.
            logger = IntermediaryAssembly.DI.TryGetLogger<DoIt>();
        }

        public void DoWork()
        {
            logger.LogDebug("Doing work...");
        }
    }
}

Packages used:

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.1" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
    <PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
    <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
  </ItemGroup>

Actually, the mistake you are making is simply not resolving the class the logger is being injected into. you need to make sure that in the startup file, you configure Logging for service injection:

services.AddLogging();

And in the class you are injecting, just do basic constructor injection, typing ILogger to the hosting class:

private readonly ILogger<DoIt> _logger;

public DoIt(ILogger<DoIt> logger)
{
   _logger = logger;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM