如何为 .NET 库配置具有依赖注入的日志记录提供程序

Posted

技术标签:

【中文标题】如何为 .NET 库配置具有依赖注入的日志记录提供程序【英文标题】:How to configure logging provider with dependency injection for .NET library 【发布时间】:2020-12-27 20:01:34 【问题描述】:

我有一个典型的场景,我正在开发一个 .NET Core 库并希望调用应用程序能够有选择地配置日志记录选项。

理想情况下,我想对库中每个类的ILogger 属性使用依赖注入。我无法解决的是如何在一个程序集(应用程序可执行文件)中初始化 DI 容器,并在另一个程序集(库程序集)中使用它。

我能想到的最好的例子是下面的示例,其中 DI 容器在执行程序集中初始化,然后存储在库随后访问的 中间 程序集中。我的问题是是否有一种方法可以不使用 intermediary 程序集并避免使用可能需要也可能不需要的日志实例污染库构造函数?

在下面的人为示例中,每个命名空间代表一个单独的程序集。库程序集对应用程序程序集一无所知。

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) MessageNewLineException")
                .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...");
        
    

使用的包:

  <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>

【问题讨论】:

这不是依赖注入,而是服务位置。这就是您发现自己需要IntermediaryAssembly 来调用其服务定位器的原因。如果您使用 DI,LibraryAssembly.DoIt 根本不会调用 IntermediaryAssembly.DI.TryGetLogger&lt;DoIt&gt;,因为那不是它的责任。 对,那么如何跨程序集边界使用 DI? 好吧,你图书馆的消费者必须给你一个ILogger&lt;DoIt&gt;或者一个记录器工厂。 DI 只是意味着参数化。它的成员不应该是 static,因为不清楚实例化 DoIt 会导致修改现有实例并破坏可测试性。 好的,明白了,我确实想避免应用程序必须将“管道”参数传递给库。 ILogger 成员不应该是静态的,我的错误,我已经调整了。 如果不了解DoIt 在现实世界场景中的使用情况,就很难判断什么是合适的或符合人体工程学的。例如,在MainAssembly 中,您可以将serviceCollection.AddSingleton&lt;DoIt&gt;(); 修改为DoIt 的ctor 为public DoIt(ILogger&lt;DoIt&gt; logger) =&gt; this.logger = logger;。但很难知道这是否合适。 【参考方案1】:

实际上,您所犯的错误只是没有解决注入记录器的类。您需要确保在启动文件中为服务注入配置 Logging:

services.AddLogging();

在你要注入的类中,只需进行基本的构造函数注入,在宿主类中输入 ILogger:

private readonly ILogger<DoIt> _logger;

public DoIt(ILogger<DoIt> logger)

   _logger = logger;

【讨论】:

以上是关于如何为 .NET 库配置具有依赖注入的日志记录提供程序的主要内容,如果未能解决你的问题,请参考以下文章

如何为 .NET Core 3.0 中 WPF 配置依赖注入 ?

如何为构造函数包含 HttpClient 和一些字符串的类设置一些 .NET Core 依赖注入?

当它具有也是 aar 库的依赖项时,如何为 android 库生成 javadoc?

.NET Core 控制台应用程序,如何为每个环境配置 appSettings?

如何为 grails 插件配置日志记录?

如何为 Kafka 生产者配置日志记录?