Mic*_*tum 93 .net c# logging .net-core .net-standard
这可能与Passog ILogger或ILoggerFactory与AspNet Core中的构造函数有些关联?但是,这特别是关于库设计,而不是关于使用这些库的实际应用程序如何实现其日志记录.
我写一个.NET 2.0标准库将通过的NuGet安装,并允许使用图书馆的人得到一些调试信息,我根据Microsoft.Extensions.Logging.Abstractions允许标准化的记录器被注入.
但是,我看到了多个接口,Web上的示例代码有时会使用ILoggerFactory并在类的ctor中创建一个记录器.还有ILoggerProvider看起来像Factory的只读版本,但实现可能会也可能不会实现两个接口,所以我必须选择.(工厂似乎比提供商更常见).
我见过的一些代码使用了非泛型ILogger接口,甚至可能共享同一个记录器的一个实例,有些代码接受ILogger<T>他们的ctor并期望DI容器支持开放泛型类型或显式注册ILogger<T>我的库中的每个变体使用.
现在,我确实认为这ILogger<T>是正确的方法,也许是一个不接受该论证而只是通过Null Logger的ctor.这样,如果不需要记录,则不使用任何记录.然而,一些DI容器选择了最大的ctor,因此无论如何都会失败.
我很好奇我应该在这里做什么来为用户创造最少的头痛,同时如果需要仍然允许适当的日志记录支持.
Hoo*_*ini 82
我们有3个接口:ILogger,ILoggerProvider和ILoggerFactory.让我们看看源代码,找出他们的职责:
ILogger:主要职责是编写给定日志级别的日志消息.
ILoggerProvider:只有一个责任,那就是创造一个ILoggerProvider.
ILogger:有2个职责,创建一个ILoggerFactory并添加一个
ILoggerProviders.
请注意,我们可以向工厂注册一个或多个提供程序(控制台,文件等).当我们使用工厂创建记录器时,它使用已注册的提供程序来创建相应的记录器.
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
Run Code Online (Sandbox Code Playgroud)
因此Logger维护一个记录器列表,并将日志消息写入所有记录器.查看Logger源代码,我们可以确认它ILogger有一个数组Logger(即LoggerInformation []),同时它正在实现ILoggers接口.
MS文档提供了两种注入记录器的方法:
1.注射工厂:
Run Code Online (Sandbox Code Playgroud)public TodoController(ITodoRepository todoRepository, ILoggerFactory logger) { _todoRepository = todoRepository; _logger = logger.CreateLogger("TodoApi.Controllers.TodoController"); }
使用Category = TodoApi.Controllers.TodoController创建一个Logger.
2.注入通用
LoggerInformation[]:Run Code Online (Sandbox Code Playgroud)public TodoController(ITodoRepository todoRepository, ILogger<TodoController> logger) { _todoRepository = todoRepository; _logger = logger; }
创建一个具有Category =完全限定类型名称T的记录器
在我看来,令文档混淆的是它没有提到注入非泛型的任何内容ILogger.在上面的相同示例中,我们注入了非泛型ILogger<T>,但它并没有解释为什么我们不会这样做ILogger.
据马克·西曼:
注入构造函数应该只接收依赖项.
将工厂注入Controller并不是一个好方法,因为Controller不负责初始化Logger(违反SRP).同时注入通用ITodoRepository会增加不必要的噪音.请参阅Steven在Simple Injector博客上的帖子
什么应该注入(至少根据上面的文章)是非通用的ILogger,但是,这不是微软的内置DI容器可以做的事情,你需要使用第三方DI库.
这是尼古拉·马洛维奇的另一篇文章,他解释了他对IoC的5条法则.
尼古拉的IoC第四定律
除了接受一组自己的依赖项之外,正在解析的类的每个构造函数都不应该有任何实现.
dav*_*owl 26
除了ILoggerProvider之外,这些都是有效的.ILogger和ILogger <T>是你应该用于记录的东西.要获得ILogger,请使用ILoggerFactory.ILogger <T>是获取特定类别的记录器的快捷方式(类型的快捷方式作为类别).
当您使用ILogger执行日志记录时,每个注册的ILoggerProviders都有机会处理该日志消息.使用代码直接调用ILoggerProvider并不是真的有效.
小智 20
ILoggerFactory或者ILoggerFactory<T>是要走的路。我以这样的方式编写类,即控制消息的内容和严重性(有时是类别),同时允许消费者选择他们想要的任何日志记录实现,或者如果他们选择的话,根本不选择。
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
public class MyClass
{
private readonly ILogger _logger;
public MyClass(
..., /* required deps */
..., /* other optional deps */
ILoggerFactory? loggerFactory)
{
_logger = loggerFactory?.CreateLogger<MyClass>()
?? NullLoggerFactory.Instance.CreateLogger<MyClass>();
}
}
Run Code Online (Sandbox Code Playgroud)
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
public class MyClass<T>
{
private readonly ILogger<T> _logger;
public MyClass<T>(
..., /* required deps */
..., /* other optional deps */
ILoggerFactory? loggerFactory)
{
_logger = loggerFactory?.CreateLogger<T>()
?? NullLoggerFactory.Instance.CreateLogger<T>();
}
}
Run Code Online (Sandbox Code Playgroud)
CreateLogger<T>()非通用。CreateLogger("")ILogger, 或ILogger<T>,但我建议这是最通用/通用的方式,它可以为您提供最多的选择,而不会影响消费者。这ILogger<T>是为DI制作的实际内容.ILogger的出现是为了帮助您更轻松地实现工厂模式,而不是您自己编写所有DI和Factory逻辑,这是asp.net核心中最明智的决策之一.
你可以选择:
ILogger<T>如果您需要在代码中使用工厂和DI模式,或者您可以使用ILogger,来实现简单的日志记录而无需DI.
鉴于此,ILoggerProvider它只是处理每个已注册日志消息的桥梁.没有必要使用它,因为它不会影响您应该干预代码的任何内容.它会监听已注册的ILoggerProvider并处理消息.就是这样.
默认方法是ILogger<T>. 这意味着在日志中,来自特定类的日志将清晰可见,因为它们将包含完整的类名作为上下文。例如,如果您的班级的全名是,MyLibrary.MyClass您将在该班级创建的日志条目中得到它。例如:
MyLibrary.MyClass:Information: 我的信息日志
ILoggerFactory如果要指定自己的上下文,则应使用。例如,您库中的所有日志都具有相同的日志上下文,而不是每个类。例如:
loggerFactory.CreateLogger("MyLibrary");
Run Code Online (Sandbox Code Playgroud)
然后日志将如下所示:
MyLibrary:Information: 我的信息日志
如果您在所有类中都这样做,那么所有类的上下文将只是 MyLibrary。我想如果您不想在日志中公开内部类结构,您会想要为库这样做。
关于可选的日志记录。我认为您应该始终在构造函数中要求 ILogger 或 ILoggerFactory 并将其留给库的使用者将其关闭或提供一个在依赖注入中不执行任何操作的 Logger 如果他们不想记录日志。在配置中关闭特定上下文的日志记录非常容易。例如:
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"MyLibrary": "None"
}
}
}
Run Code Online (Sandbox Code Playgroud)
坚持这个问题,我认为ILogger<T>是正确的选择,考虑到其他选择的缺点:
ILoggerFactory强制您的用户将可变全局记录器工厂的控制权交给您的类库.此外,通过接受ILoggerFactory您的类现在可以使用CreateLogger方法写入任何类别名称的日志.虽然ILoggerFactory通常在DI容器中作为单例提供,但我作为用户会怀疑为什么任何库都需要使用它.ILoggerProvider.CreateLogger看起来像它,但它不是用于注射.它被使用,ILoggerFactory.AddProvider因此工厂可以创建聚合ILogger,写入ILogger从每个注册的提供者创建的多个.当您检查实现时,这一点很清楚LoggerFactory.CreateLoggerILogger也看起来像是要走的路,但是使用.NET Core DI是不可能的.这实际上听起来就像他们需要首先提供的原因ILogger<T>.毕竟,ILogger<T>如果我们从这些课程中选择,我们没有更好的选择.
另一种方法是注入包含非泛型的其他东西,ILogger在这种情况下应该是非泛型的.我们的想法是,通过使用您自己的类包装它,您可以完全控制用户如何配置它.
我更愿意保持简单并注入非通用 ILogger
这似乎是非默认行为 - 但很容易与以下内容连接:
services.AddTransient(s => s.GetRequiredService<ILoggerFactory>().CreateLogger(""));
Run Code Online (Sandbox Code Playgroud)
对于图书馆设计,好的方法是:
1.不要强迫消费者向你的课程注入记录器.只需创建另一个传递NullLoggerFactory的ctor.
class MyClass
{
private readonly ILoggerFactory _loggerFactory;
public MyClass():this(NullLoggerFactory.Instance)
{
}
public MyClass(ILoggerFactory loggerFactory)
{
this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
}
}
Run Code Online (Sandbox Code Playgroud)
2.限制在创建记录器时使用的类别数,以允许使用者轻松配置日志过滤.
this._loggerFactory.CreateLogger(Consts.CategoryName)
| 归档时间: |
|
| 查看次数: |
16684 次 |
| 最近记录: |