这可能在某种程度上与Pass ILogger or ILoggerFactory to constructors in AspNet Core?有关,但是这具体是关于Library Design,而不是关于使用这些库的实际应用程序如何实现其日志(log)记录.

我正在写一篇文章.net标准2.0库将通过Nuget安装,为了让使用该库的人获得一些调试信息,我依靠Microsoft.Extensions.Logging.Abstractions来允许注入标准化的记录器.

然而,我看到了多个接口,web上的示例代码有时会使用ILoggerFactory,并在类的ctor中创建一个记录器.还有ILoggerProvider,它看起来像工厂的只读版本,但实现可能实现,也可能不实现这两个接口,所以我不得不 Select .(工厂似乎比供应商更常见).

我看到的一些代码使用非泛型ILogger接口,甚至可能共享同一个记录器的一个实例,一些代码在其ctor中使用ILogger<T>,并期望DI容器支持开放泛型类型或显式注册库使用的每个ILogger<T>变体.

现在,我确实认为ILogger<T>是正确的方法,可能是一个不接受那个参数,只传递一个空记录器的ctor.这样,如果不需要日志(log)记录,则不使用日志(log)记录.然而,一些DI容器 Select 了最大的ctor,因此无论如何都会失败.

我很好奇我会在这里做些什么来给用户带来最少的麻烦,同时仍然允许适当的日志(log)支持(如果需要的话).

推荐答案

释义

我们有3个接口:ILoggerILoggerProviderILoggerFactory.让我们来看看这source code人,看看他们的责任:

ILogger:负责编写给定Log Level的日志(log)消息.

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

ILoggerFactory:您可以向工厂注册一个或多个ILoggerProvider,工厂反过来使用所有ILoggerProvider来创建ILogger的实例.ILoggerFactoryCollection 了ILoggerProviders本书.

在下面的示例中,我们向工厂注册了两个Provider (控制台和文件).当我们创建一个记录器时,工厂使用这两个提供程序来创建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

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


依赖项注入

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

1. Injecting the factory:

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

creates a Logger with Category = TodoApi.Controllers.TodoController

2. Injecting a generic 100:

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

creates a logger with Category = fully qualified type name of TodoController


在我看来,让文档混乱的是,它没有提到任何关于注入非通用的ILogger.在上面的同一个例子中,我们正在注入一个非通用的ITodoRepository,但它并不能解释为什么我们没有对ILogger进行同样的操作.

根据Mark Seemann:

注入构造函数只需接收

将工厂注入控制器不是一个好方法,因为初始化记录器不是控制器的责任(违反SRP).同时,注入通用ILogger<T>会增加不必要的噪音.详见Simple Injector's博客:What’s wrong with the ASP.NET Core DI abstraction?

应该注入的(至少根据上面的文章)是非通用的ILogger,但这不是微软内置的DI容器所能做到的,你需要使用第三方DI库.These two个文档解释了如何使用第三方库.净核心.


这是尼古拉·马洛维奇的another article页,他在其中解释了国际奥委会的5条规则.

Nikola’s 4th law of IoC

正在解析的类的每个构造函数都不应该有

.net相关问答推荐

获取Ef-Core集合的DeleteBehavior

.NET Async / Await:状态机如何知道何时继续执行?

FileHelpers 未使用正确的日期格式

.Net MAUI Android 无法与 API localhost 对话

如何在 C# 中创建表达式树来表示String.Contains("term")?

LINQ:确定两个序列是否包含完全相同的元素

处理序列没有元素异常

struct 中需要覆盖什么以确保平等正常运行?

.net 服务总线建议?

HashSet 是否保留插入顺序?

如何对无法加载的 VSTO 插件进行故障排除?

日期时间是什么意思?在 C# 中是什么意思?

在 .NET 中填充整数列表

.NET 中的对象引用有多大?

.NET 的黄瓜替代品

强制 XmlSerializer 将 DateTime 序列化为 'YYYY-MM-DD hh:mm:ss'

C# List<> 按 x 然后 y 排序

如何在 Dapper.Net 中编写一对多查询?

MailMessage,Sender 和 From 属性的区别

判断数据表中是否包含空值的最佳方法