我对学习如何在依赖注入平台中注入日志感兴趣。虽然下面的链接和我的示例是关于log4net和Unity的,但我不一定会使用它们。对于依赖注入/IOC,我可能会使用MEF,因为这是项目(大型)正在采用的标准。
我非常新于依赖注入/IOC,并且对C#和.NET也非常陌生(过去10年左右都是VC6和VB6)。我已经调查了许多logging解决方案,所以我认为我对它们的特性有一定的了解。我只是不太熟悉获取一个依赖项注入(或者更正确地说,获取抽象版本的依赖项注入)的实际机制。
我看到了其他与logging和/或依赖注入相关的帖子,比如:dependency injection and logging interfaces
What would a Log4Net Wrapper class look like?
again about log4net and Unity IOC config
我的问题与“如何使用IOC工具注入日志平台xxx?”无关。相反,我对人们如何处理包装logging平台(通常但并不总是建议)和配置(即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,我可能希望基于TraceSource实现一个基于接口的记录器(甚至可以使用“通用”的ILogger / ILog接口),以便进行注入。你会实现这个接口,并只使用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实例。因此,与传统意义上的注入不同(通过构造函数、属性或成员数据),日志记录依赖性是明确解决的。这是一个好的还是坏的依赖注入方式。
我将其标记为社区wiki,因为它似乎不是一个具有明确答案的问题。如果有人有不同看法,请随时更改。
谢谢任何帮助!
x.Request.ParentContext.Plan.Type.FullName
来获取具体的类名,而不是接口名。 - Chadwick