我有兴趣了解更多有关人们如何使用依赖注入平台注入日志记录的信息。尽管下面的链接和我的示例引用了 log4net 和 Unity,但我不一定会使用其中任何一个。对于依赖注入/IOC,我可能会使用 MEF,因为这是项目(大型)其余部分所采用的标准。
我对依赖注入/ioc 非常陌生,对 C# 和 .NET 也很陌生(在过去 10 年左右的 VC6 和 VB6 之后,我在 C#/.NET 中编写了很少的生产代码)。我对现有的各种日志记录解决方案进行了大量调查,因此我认为我对它们的功能集有很好的掌握。我只是不太熟悉注入一个依赖项的实际机制(或者,也许更“正确”,获取注入一个依赖项的抽象版本)。
我看过其他与日志记录和/或依赖项注入相关的帖子,例如:依赖注入和日志记录接口 https://stackoverflow.com/questions/522370/dependency-injection-and-logging-interfaces
记录最佳实践 https://stackoverflow.com/questions/576185/logging-best-practices
Log4Net Wrapper 类会是什么样子? https://stackoverflow.com/questions/166438/what-would-a-log4net-wrapper-class-look-like
再次关于 log4net 和 Unity IOC 配置 https://stackoverflow.com/questions/1320844/again-about-log4net-and-unity-ioc-config
我的问题与“如何使用 ioc 工具 yyy 注入日志平台 xxx?”没有具体关系。相反,我感兴趣的是人们如何处理包装日志平台(通常但并不总是推荐)和配置(即 app.config)。例如,以 log4net 为例,我可以(在 app.config 中)配置多个记录器,然后以使用如下代码的标准方式获取这些记录器(无需依赖注入):
private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
或者,如果我的记录器不是为类命名,而是为功能区域命名,我可以这样做:
private static readonly ILog logger = LogManager.GetLogger("Login");
private static readonly ILog logger = LogManager.GetLogger("Query");
private static readonly ILog logger = LogManager.GetLogger("Report");
所以,我想我的“要求”会是这样的:
我想使我的产品来源免受对日志平台的直接依赖。
我希望能够通过某种依赖注入(可能是 MEF)直接或间接地解析特定的命名记录器实例(可能在同一命名实例的所有请求者之间共享同一实例)。
我不知道我是否会将此称为硬要求,但我希望能够按需获取命名记录器(与类记录器不同)。例如,我可能根据类名称为我的类创建一个记录器,但一种方法需要特别繁重的诊断,我想单独控制这些诊断。换句话说,我可能希望单个类“依赖”两个单独的记录器实例。
让我们从第一点开始。我读过很多文章,主要是在 stackoverflow 上,关于换行是否是一个好主意。请参阅上面的“最佳实践”链接并转到杰弗里·汉丁 https://stackoverflow.com/users/55637/jeffrey-hantin关于为什么包装 log4net 不好的一种观点的评论。如果你确实进行了包装(并且如果你可以有效地包装),你会为了注入/删除直接依赖而严格包装吗?或者您是否还会尝试抽象出部分或全部 log4net app.config 信息?
假设我想使用 System.Diagnostics,我可能想实现一个基于接口的记录器(甚至可能使用“通用”ILogger/ILog 接口),可能基于 TraceSource,以便我可以注入它。您是否会通过 TraceSource 实现该接口,并按原样使用 System.Diagnostics app.config 信息?
像这样的东西:
public class MyLogger : ILogger
{
private TraceSource ts;
public MyLogger(string name)
{
ts = new TraceSource(name);
}
public void ILogger.Log(string msg)
{
ts.TraceEvent(msg);
}
}
并像这样使用它:
private static readonly ILogger logger = new MyLogger("stackoverflow");
logger.Info("Hello world!")
继续讨论第二点...如何解析特定的命名记录器实例?我是否应该只利用我选择的日志平台的 app.config 信息(即根据 app.config 中的命名方案解析记录器)?那么,在 log4net 的情况下,我是否更愿意“注入”LogManager(请注意,我知道这是不可能的,因为它是静态对象)?我可以包装 LogManager(将其称为 MyLogManager),给它一个 ILogManager 接口,然后解析 MyLogManager.ILogManager 接口。我的其他对象可能对 ILogManager 有依赖关系(用 MEF 的说法是导入)(从实现它的程序集中导出)。现在我可以拥有这样的对象:
public class MyClass
{
private ILogger logger;
public MyClass([Import(typeof(ILogManager))] logManager)
{
logger = logManager.GetLogger("MyClass");
}
}
任何时候调用 ILogManager 时,它都会直接委托给 log4net 的 LogManager。或者,包装的 LogManager 是否可以获取基于 app.config 获取的 ILogger 实例,并按名称将它们添加到(?)MEF 容器中。稍后,当请求同名的记录器时,将查询包装的 LogManager 以获取该名称。如果有ILogger,就这样解决。如果 MEF 可以做到这一点,这样做有什么好处吗?
在这种情况下,实际上,只有 ILogManager 被“注入”,并且它可以按照 log4net 通常的方式分发 ILogger 实例。这种类型的注入(本质上是工厂)与注入命名记录器实例相比如何?这确实允许更轻松地利用 log4net(或其他日志平台)的 app.config 文件。
我知道我可以像这样从 MEF 容器中获取命名实例:
var container = new CompositionContainer(<catalogs and other stuff>);
ILogger logger = container.GetExportedValue<ILogger>("ThisLogger");
但是如何将命名实例放入容器中呢?我知道基于属性的模型,在该模型中我可以有不同的 ILogger 实现,每个实现都被命名(通过 MEF 属性),但这并没有真正帮助我。有没有一种方法可以创建类似 app.config (或其中的一部分)的内容,该内容可以按名称列出记录器(所有相同的实现)并且 MEF 可以读取?是否可以/应该有一个中央“管理器”(如 MyLogManager)通过底层 app.config 解析命名记录器,然后将解析后的记录器插入 MEF 容器?这样,有权访问同一 MEF 容器的其他人就可以使用它(尽管如果 MyLogManager 不知道如何使用 log4net 的 app.config 信息,容器似乎无法直接解析任何指定的记录器)。
这件事已经过去很久了。我希望它是连贯的。请随意分享有关如何将日志记录平台(我们最有可能考虑 log4net、NLog 或基于 System.Diagnostics 构建的其他东西(希望是薄的))依赖注入到您的应用程序中的任何具体信息。
您是否注入了“管理器”并让它返回记录器实例?
您是否在自己的配置部分或 DI 平台的配置部分中添加了一些自己的配置信息,以便更容易/可能直接注入记录器实例(即使您的依赖项位于 ILogger 而不是 ILogManager)。
拥有一个静态或全局容器,其中包含 ILogManager 接口或一组命名的 ILogger 实例怎么样?因此,日志依赖项不是传统意义上的注入(通过构造函数、属性或成员数据),而是根据需要显式解决。这是依赖注入的好还是坏方法?
我将其标记为社区维基,因为它看起来不像是一个有明确答案的问题。如果有人有其他感觉,请随意更改。
谢谢你的帮助!