c# - 我应该将 ILogger、ILogger<T>、ILoggerFactory 还是 ILoggerProvider 作为库使用?

标签 c# .net logging .net-core .net-standard

这可能与Pass ILogger or ILoggerFactory to constructors in AspNet Core?有点关系,但是这是专门针对库设计的,而不是关于使用这些库的实际应用程序如何实现其日志记录的。

我正在编写一个将通过 Nuget 安装的 .net Standard 2.0 库,并允许使用该库的人获得一些调试信息,我依赖于 Microsoft.Extensions.Logging.Abstractions允许注入(inject)标准化的记录器。

但是,我看到了多个接口(interface),网络上的示例代码有时会使用 ILoggerFactory并在类的 ctor 中创建一个记录器。还有 ILoggerProvider它看起来像工厂的只读版本,但实现可能会也可能不会同时实现这两个接口(interface),所以我必须选择。 (Factory 似乎比 Provider 更常见)。

我见过的一些代码使用了非泛型 ILogger接口(interface),甚至可能共享同一记录器的一个实例,有些采用 ILogger<T>在他们的构造函数中,并期望 DI 容器支持开放的泛型类型或每个 ILogger<T> 的显式注册我的图书馆使用的变体。

现在,我确实认为 ILogger<T>是正确的方法,也许是一个不接受该参数而只是传递 Null Logger 的 ctor。这样,如果不需要日志记录,则不使用任何日志记录。然而,一些 DI 容器会选择最大的 ctor,因此无论如何都会失败。

我很好奇我应该在这里做些什么来尽量减少用户的头痛,同时如果需要仍然允许适当的日志记录支持。

最佳答案

定义

我们有 3 个接口(interface):ILogger , ILoggerProviderILoggerFactory .让我们看看source code找出他们的职责:

ILogger:负责写入给定日志级别的日志消息。

ILoggerProvider:负责创建ILogger的实例(您不应该直接使用 ILoggerProvider 来创建记录器)

ILoggerFactory:可以注册一个或多个ILoggerProvider与工厂一起使用,工厂又使用所有这些来创建 ILogger 的实例. ILoggerFactory持有 ILoggerProviders 的集合.

在下面的示例中,我们向工厂注册了 2 个提供程序(控制台和文件)。当我们创建记录器时,工厂使用这两个提供程序来创建 Logger 的实例:

ILoggerFactory factory = new LoggerFactory().AddConsole();    // add console provider
factory.AddProvider(new LoggerFileProvider("c:\\log.txt"));   // add file provider
Logger logger = factory.CreateLogger(); // creates a console logger and a file logger

因此记录器本身正在维护一个 ILogger 的集合s,并将日志消息写入所有这些。 Looking at Logger source code我们可以确认Logger有一个数组 ILoggers (即 LoggerInformation[] ),同时它正在实现 ILogger界面。


依赖注入(inject)

MS documentation提供了两种注入(inject)记录器的方法:

1. Injecting the factory:

public TodoController(ITodoRepository todoRepository, ILoggerFactory logger)
{
    _todoRepository = todoRepository;
    _logger = logger.CreateLogger("TodoApi.Controllers.TodoController");
}

创建一个 Category = TodoApi.Controllers.TodoController 的 Logger

2. Injecting a generic ILogger<T>:

public TodoController(ITodoRepository todoRepository, ILogger<TodoController> logger)
{
    _todoRepository = todoRepository;
    _logger = logger;
}

创建一个 Category = TodoController 完全限定类型名称的记录器


在我看来,文档令人困惑的原因在于它没有提及任何关于注入(inject)非通用 ILogger 的内容。 .在上面的同一个例子中,我们注入(inject)了一个非泛型 ITodoRepository然而,它并没有解释为什么我们不对 ILogger 做同样的事情。 .

根据 Mark Seemann :

An Injection Constructor should do no more than receiving the dependencies.

将工厂注入(inject)到 Controller 中不是一个好方法,因为初始化 Logger 不是 Controller 的责任(违反 SRP)。同时注入(inject)一个泛型ILogger<T>增加不必要的噪音。有关详细信息,请参阅 Simple Injector 的 博客:What’s wrong with the ASP.NET Core DI abstraction?

应该注入(inject)的(至少根据上面的文章)是一个非泛型 ILogger ,但是,这不是 Microsoft 的内置 DI 容器可以做到的,您需要使用第 3 方 DI 库。 These two文档解释了如何将 3rd 方库与 .NET Core 结合使用。


这是 another article作者 Nikola Malovic,他在其中解释了 IoC 的 5 条法则。

Nikola’s 4th law of IoC

Every constructor of a class being resolved should not have any implementation other than accepting a set of its own dependencies.

关于c# - 我应该将 ILogger、ILogger<T>、ILoggerFactory 还是 ILoggerProvider 作为库使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51345161/

相关文章:

c# - 获取像素值 "Under"当前窗口

c# - dotnet pack 命令和 nuspec 文件不包括项目的 DLL

java - 如何在运行时更改日志级别而不重新启动 Vert.x 应用程序

php - Symfony 2.1 - 在 Controller 中切换 Monolog channel

python - cx-freeze 与 .net 的 python 不兼容吗?

python - 如何在 Python 中记录源文件名和行号

c# - 使用网络上的主机名获取 ip 地址 asp.net

c# - 我如何知道 WindowsFormsHost 何时在 WPF 中调整大小?

c# - LINQ 中的 .Take 方法是否有通配符?

.net - 关于 "remoting"的最佳实践,一个带有 WCF 的 .NET 类库