我有兴趣了解更多关于人们如何使用依赖注入平台注入日志的信息。尽管下面的链接和我的示例引用了 log4net 和 Unity,但我不一定会使用其中任何一个。对于依赖注入/IOC,我可能会使用 MEF,因为这是项目的其余部分(大型)正在制定的标准。
我对依赖注入/ioc 很陌生,对 C# 和 .NET 也很陌生(在过去 10 年左右的 VC6 和 VB6 之后,我在 C#/.NET 中编写的生产代码很少)。我对现有的各种日志记录解决方案进行了大量调查,因此我认为我对它们的功能集有一个不错的处理。我只是对注入一个依赖项的实际机制不够熟悉(或者,也许更“正确”,注入一个依赖项的抽象版本)。
我看过其他与日志和/或依赖注入相关的帖子,例如: 依赖注入和日志接口
我的问题与“如何使用 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)直接或间接地解析一个特定的命名记录器实例(可能在同一个命名实例的所有请求者之间共享同一个实例)。
我不知道我是否会将此称为硬性要求,但我希望能够按需获取命名记录器(不同于类记录器)。例如,我可能会根据类名为我的类创建一个记录器,但是一种方法需要特别繁重的诊断,我想单独控制这些诊断。换句话说,我可能希望单个类“依赖”两个单独的记录器实例。
让我们从第 1 条开始。我已经阅读了很多文章,主要是关于 stackoverflow 的文章,关于包装是否是个好主意。请参阅上面的“最佳实践”链接并转到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!")
转到第 2 点...如何解析特定的命名记录器实例?我是否应该只利用我选择的日志记录平台的 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 实例,并按名称将它们添加到(a?)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 实例的静态或全局容器怎么样?因此,不是传统意义上的注入(通过构造函数、属性或成员数据),而是按需显式解决日志记录依赖关系。这是依赖注入的好方法还是坏方法。
我将其标记为社区 wiki,因为它似乎不是一个有明确答案的问题。如果有人觉得不一样,请随时更改。
谢谢你的帮助!