依赖注入和命名记录器

我有兴趣进一步了解人们如何使用依赖注入平台注入日志。 虽然下面的链接和我的示例都提到了log4net和Unity,但我不一定会使用其中的任何一个。 对于依赖注入/ IOC,我可能会使用MEF,因为这是项目其他部分(大)正在解决的标准。

我对依赖注入/ ioc非常陌生,并且对C#和.NET来说很新颖(在VC6和VB6的过去10多年中,在C#/ .NET中已经编写了很少的生产代码)。 我已经对各种各样的日志解决方案进行了很多调查,所以我认为我对他们的功能集有很好的把握。 我只是不太熟悉用实际的机制来获得一个依赖注入(或者,也许更“正确地”,得到一个注入的依赖注入的抽象版本)。

我已经看到其他与日志和/或依赖注入相关的帖子,如:依赖注入和日志接口

记录最佳实践

Log4Net Wrapper类会是什么样子?

再次关于log4net和Unity IOC配置

我的问题没有特别与“如何使用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!")
    

    前进到第二个数字...如何解决一个特定的命名记录器实例? 我是否应该利用我选择的日志平台的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实例。 因此,不是按照常规的意义(通过构造函数,属性或成员数据)进行注入,而是根据需要明确解决日志依赖性问题。 这是依赖注入的好方法还是坏方法?

    我将它标记为社区维基,因为它看起来不像是有确切答案的问题。 如果任何人有其他感觉,请随时更改。

    谢谢你的帮助!


    我正在使用Ninject来解析记录器实例的当前类名称,如下所示:

    kernel.Bind<ILogger>().To<NLogLogger>()
      .WithConstructorArgument("currentClassName", x => x.Request.ParentContext.Request.Service.FullName);
    

    NLog实现的构造函数可能如下所示:

    public NLogLogger(string currentClassName)
    {
      _logger = LogManager.GetLogger(currentClassName);
    }
    

    我猜,这种方法也应该与其他国际奥委会容器一起工作。


    也可以使用Common.Logging外观或简单记录外观。

    这两种都使用服务定位器样式来检索ILogger。

    坦率地说,日志是我在自动注入中看不到什么价值的依赖之一。

    我需要日志服务的大部分类都是这样的:

    public class MyClassThatLogs {
        private readonly ILogger log = Slf.LoggerService.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName);
    
    }
    

    通过使用简单日志门面,我将一个项目从log4net切换到NLog,并且除了使用NLog的应用程序日志记录之外,我还添加了使用log4net的第三方库的日志记录。 也就是说,立面给了我们很好的帮助。

    难以避免的一个警告是特定于一个日志框架或另一个日志框架的功能的丧失,或许是最常见的例子是自定义日志记录级别。


    这对于任何试图了解如何在要向其注入的记录器提供日志记录平台(例如log4net或NLog)时注入记录器依赖项的人都有好处。 我的问题是,当我知道特定ILogger的解析将取决于知道依赖于ILogger的类的类型时,我无法理解如何创建一个类(例如MyClass)依赖于ILogger类型的接口(例如MyClass)。 DI / IoC平台/容器如何获得正确的ILogger?

    那么,我已经查看了Castle和NInject的来源,并看到了它们的工作原理。 我也看了AutoFac和StructureMap。

    Castle和NInject都提供了日志记录的实现。 两者都支持log4net和NLog。 Castle还支持System.Diagnostics。 在这两种情况下,当平台解析给定对象的依赖关系时(例如,当平台创建MyClass并且MyClass依赖于ILogger时),它将创建依赖项(ILogger)委托给ILogger“provider”(解析器可能更多通用术语)。 然后,ILogger提供程序的实现负责实际实例化一个ILogger实例并将其返回,然后将其注入到依赖类(例如MyClass)中。 在这两种情况下,提供者/解析器都知道相关类的类型(例如MyClass)。 所以,当MyClass被创建并且它的依赖被解析时,ILogger“解析器”知道这个类是MyClass。 在使用Castle或NInject提供的日志记录解决方案的情况下,这意味着日志解决方案(作为log4net或NLog的封装实现)获取类型(MyClass),因此它可以委托给log4net.LogManager.GetLogger()或NLog.LogManager.GetLogger()。 (对于log4net和NLog不是100%确定的语法,但你明白了)。

    尽管AutoFac和StructureMap没有提供日志记录工具(至少我可以通过查看),但他们确实提供了实现自定义解析器的功能。 所以,如果你想编写你自己的日志抽象层,你也可以编写一个相应的自定义解析器。 这样,当容器想要解析ILogger时,解析器将被用来获得正确的ILogger,并且它可以访问当前的上下文(即当前正在满足什么对象的依赖关系 - 哪个对象依赖于ILogger)。 获取对象的类型,并准备将ILogger的创建委托给当前配置的日志平台(您可能已经抽象出了一个接口并为其编写了解析器)。

    所以,我怀疑需要几个关键点,但以前我并没有完全掌握:

  • 不管怎样,最终DI容器必须意识到要使用哪种记录平台。 通常这是通过指定“ILogger”由特定于日志记录平台的“解析器”(因此,Castle具有log4net,NLog和System.Diagnostics“解析器”(等等))来解决。 可以通过配置文件或以编程方式完成使用哪个解析器的规范。

  • 解析器需要知道依赖项(ILogger)正在解析的上下文。 也就是说,如果MyClass已经创建并且依赖于ILogger,那么当解析器试图创建正确的ILogger时,它(解析器)必须知道当前的类型(MyClass)。 这样,解析器就可以使用底层的日志记录实现(log4net,NLog等)来获取正确的日志记录。

  • 这些观点对于那些DI / IoC用户来说可能是显而易见的,但是我现在刚刚进入这个领域,所以我花了一段时间才弄清楚了这一点。

    有一件事我还没有弄清楚,MEF如何或如果这样做是可能的。 我可以拥有一个依赖于接口的对象,然后在MEF创建对象并解析接口/依赖关系之后执行我的代码? 所以,假设我有这样的课程:

    public class MyClass
    {
      [Import(ILogger)]
      public ILogger logger;
    
      public MyClass()
      {
      }
    
      public void DoSomething()
      {
        logger.Info("Hello World!");
      }
    }
    

    当MEF为MyClass解析导入时,我是否可以拥有一些自己的代码(通过一个属性,通过ILogger实现的额外接口,在其他地方?)执行并解析ILogger导入,基于它是MyClass当前处于上下文中,并返回一个(可能)不同的ILogger实例,而不是为YourClass检索的实例? 我是否实施某种MEF提供商?

    在这一点上,我仍然不了解MEF。

    链接地址: http://www.djcxy.com/p/77709.html

    上一篇: Dependency injection and named loggers

    下一篇: How to use log4net with Dependency Injection