在从ASP.NET核心Web应用程序调用的动态发现的.NET Core类库中注入记录器的合适模式是什么?

Tag*_*agc 10 c# logging mef .net-core asp.net-core

概观

我正在尝试将基于.NET Framework的许多项目移植到.NET Core.这涉及移植许多类库以及使用这些库的顶级控制台/ Web应用程序.

我的部分要求是我的顶级应用程序应该支持基于插件的系统,我可以通过引用这些类库的不同子集来轻松地交换功能.我用过MEF来做这个.例如,我的一个ASP.NET核心Web应用程序涉及通过一个设备与设备通信ICommunicationService,我有不同的Nuget包导出该服务的不同实现:

[Export(typeof(ICommunicationService))]
[Shared]
public sealed class UsbCommunicationService : ICommunicationService
{
}
Run Code Online (Sandbox Code Playgroud)

重新设计类库

目前,这些类库引用Common.Logging并将记录器实例化为只读静态字段:

[Export(typeof(ICommunicationService))]
[Shared]
public sealed class UsbCommunicationService : ICommunicationService
{
    ...
    private static readonly ILog Log = LogManager.GetLogger<UsbCommunicationService>();
    ....
}
Run Code Online (Sandbox Code Playgroud)

Log4Net在我的顶级应用程序中使用,并通过引用Common.Logging.Log4Net适配器来促进我的类库中的日志记录.

但是,我知道ASP.NET Core依赖于Microsoft的新日志抽象框架Microsoft.Extensions.Logging,并且ASP.NET Core应用程序应该设计为通过构造函数依赖注入记录器来支持日志记录,如下所示:

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

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

    public IActionResult Index()
    {
        _logger.LogInformation("Index action requested at {requestTime}", DateTime.Now);
        return View();
    }
}
Run Code Online (Sandbox Code Playgroud)

我不完全确定在我的新.NET Core库和应用程序中使用哪种日志框架组合(请参阅此相关文章),但我倾向于从使用切换Common.LoggingMicrosoft.Extensions.Logging我的类库中.在那种情况下,我想知道我应该如何处理记录器的实例化.这样的事情会合适吗?

using Microsoft.Extensions.Logging;
...
[ImportingConstructor]
public UsbCommunicationService(
    [Import] IUsbMessageEncoder encoder,
    [Import] IUsbMessageDecoder decoder,
    [Import] ILogger<UsbCommunicationService> logger /* Add this new import */)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

换句话说,我是否应该将需要记录的所有类库切换为在构造期间注入这些记录器?

消费类库

根据对类似问题的回答,我觉得上面详述的方法是正确的.但是,我不确定如何在ASP.NET Core应用程序中使用和正确实例化类库中定义的服务.

ASP.NET Core使用自己的依赖注入服务,该服务与MEF完全分离.我不能简单地services.AddSingleton<ICommunicationService, UsbCommunicationService>();在我的Web应用程序中编写类似的东西,原因有两个:

  1. 这个想法是支持一个基于插件的系统,其中动态发现插件,因此"核心"应用程序本身不能硬引用.

  2. ASP.NET Core的服务注入器不知道UsbCommunicationService- IUsbMessageEncoderIUsbMessageDecoder- 的其他依赖项,并且无法解析.

同样,我不能使用MEF来获取任何一个实例,UsbCommunicationService因为它无法解析引用ILogger<UsbCommunicationService>.

摘要

简而言之,我正在尝试寻找以下解决方案:

  • 便于在.NET Core库中进行日志记录,为记录提供程序提供最大的灵活性.
  • 允许使用依赖项注入将记录器提供给这些类库.
  • 允许顶级ASP.NET Core或.NET Core控制台应用程序在运行时动态发现和加载这些.NET Core库,并为它们提供所有记录器或记录器工厂,以便顶级应用程序和所有加载的插件使用常见的日志记录提供程序(例如Serilog,NLog,Log4Net等).
    • 例如,如果我想使用Log4Net ColoredConsoleAppender,我应该看到所有ASP.NET日志和类库日志都出现在同一个控制台中.

Chr*_*att 4

Microsoft.Extensions.Logging 严格来说并不是一个日志框架;这是一个门面。Debug有诸如、Trace、等的内置实现Console,但这只是为了方便。在实际使用中,您可能会插入 Serilog 之类的东西。Serilog 是实际处理日志记录的,而 Microsoft.Extensions.Logging 只是提供一种抽象的“日志记录”方式,而不必实际使您的应用程序代码显式依赖于 Serilog 的 API。

Common.Logging 也是一个外观,在本例中,您选择插入 log4net 作为实际使用的记录器。然而,这种情况是可以改变的。鉴于此,您有几种可能的路径可以选择。

  1. 在 ASP.NET Core 应用中将 Microsoft.Extensions.Logging 替换为 Common.Logging。是的,你可以这样做。就个人而言,我认为 Microsoft.Extensions.Logging更好,但您可以使用任何您喜欢的。

  2. 在类库中使用 Microsoft.Extensions.Logging 替换 Common.Logging。如果您无论如何都要重写它们,这可能是最有意义的,但就代码中需要更改的内容而言,它也涉及最多的摩擦。

  3. 为使用 Microsoft.Extensions.Logging 的 Common.Logging 编写一个适配器。诚然,这有点元,但从技术上讲,简单地使用一个外观与另一个外观配合使用,最终与特定的日志框架配合使用并没有什么问题。这就是适配器模式的全部要点。这也让您两全其美:您不需要对库或 ASP.NET Core 应用程序进行太多更改。然而,由于存在多个门面,它确实会增加代码的熵。虽然这很难说,但我实际上不认为 Common.Logging 会继续得到维护。现在微软已经有了一个几乎内置的外观,我希望看到它几乎占据主导地位。当您已经进行了一定量的返工时,最好现在就跳槽。