依赖注入和命名记录器 [英] Dependency injection and named loggers

查看:158
本文介绍了依赖注入和命名记录器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有兴趣更多地了解人们如何使用依赖注入平台注入日志记录。虽然下面的链接和我的例子指的是log4net和Unity,但我不一定要使用其中的任何一个。对于依赖注入/ IOC,我可能会使用MEF,因为项目的其余部分(大)正在解决的标准。



我对于依赖关系很新注入/ ioc,对C#和.NET来说是相当新鲜的(在VC6和VB6过去10年左右,在C#/ .NET中编写的生产代码很少)。我已经对各种日志记录解决方案进行了大量的调查,所以我认为我对它们的功能集有一个很好的处理。我只是不够熟悉,因为注入了一个依赖关系的实际机制(或者说可能更正确地获得注入的一个依赖关系的抽象版本)。



I已经看到与日志记录和/或依赖注入有关的其他帖子,如:
依赖注入和日志记录界面



记录最佳做法



Log4Net Wrapper类是什么样的?



再次关于log4net和Unity IOC配置



我的问题没有具体与如何注入记录平台xxx使用ioc工具yyy?相反,我感兴趣的是如何处理包装日志记录平台(通常但并不总是推荐)和配置(即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);

所以,我想我的需求将是这样的:


  1. 我想将我的产品来源与日志平台直接相关。


  2. 我想能够通过某种依赖注入(可能是MEF)直接或间接地解析一个特定的命名的log​​ger实例(可能在同一命名实例的所有请求者之间共享相同的实例)。


  3. 我不知道我是否会将此称为硬性要求,但是我希望能够根据需要获得一个名为logger(与类记录器不同)的能力。例如,我可以根据类名为我的类创建一个记录器,但是一种方法需要特别重的诊断,我想单独控制。换句话说,我可能希望单个类别依赖于两个单独的记录器实例。


我已经阅读了许多文章,主要是在这里的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实例,并按名称将它们添加到(??)MEF容器中。之后,当请求同名的记录器时,查询包装的LogManager。如果ILogger在那里,那就这样解决了。如果这是可能的MEF,这样做有什么好处吗?



在这种情况下,真的只有ILogManager被注入,它可以发出ILogger实例log4net通常的方式。这种类型的注入(本质上是工厂)如何与注入命名的记录器实例进行比较?这样可以更方便地使用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信息,但似乎容器将无法直接解析任何命名的记录器) / p>

这已经很久了。我希望它是一贯的。请随时分享有关注册日志记录平台的任何特定信息(我们最有可能将Log4net,NLog或者基于System.Diagnostics构建的内容(希望很薄)考虑在您的应用程序中)。你是否注入管理器并让它返回记录器实例?



您是否在自己的配置部分或DI平台的配置部分添加了一些自己的配置信息,以便更容易/可能直接注入记录器实例(即make你的依赖关系是ILogger而不是ILogManager)。



如何使静态或全局容器具有其中的ILogManager接口或其中的一组名为ILogger的实例。所以,不是以传统意义(通过构造函数,属性或成员数据)注入,而是根据需要明确地解析日志依赖关系。这是一个好的还是坏的依赖注入方式。



我把它标记为一个社区维基,因为它似乎不是一个明确的答案的问题。如果有任何人感觉不到,请随意更改。



感谢任何帮助!

解决方案

这是为了让任何人试图找出如何注入记录器依赖关系的任何人,当您要注入的记录器被提供一个日志记录平台,如log4net或NLog。我的问题是,当我知道特定ILogger的分辨率将取决于知道依赖于ILogger的类的类型时,我无法理解我如何使一个类(例如MyClass)依赖于ILogger类型的接口例如MyClass)。 DI / IoC平台/容器如何获得正确的ILogger?



嗯,我已经看了Castle和NInject的来源,看到了它们的工作原理。我也看过AutoFac和StructureMap。



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



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



所以,我怀疑有几个要点,但我以前没有完全掌握的要点是:


  1. 最终,DI容器必须是
    ,不知何故,使用什么logging
    平台。通常,这是
    ,通过指定ILogger是

    解析的
    特定于日志平台
    (因此,Castle具有log4net,NLog ,
    和System.Diagnosticsresolvers
    (等等))。使用解析器的规范
    可以通过配置文件或以编程方式完成


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




    1. 这些点对于那些DI / IoC用户来说可能是显而易见的,但我刚刚进入它,所以我花了一段时间来解决这个问题。



      我还没有想到的一件事是MEF如何或者像这样的事情。我可以有一个对象依赖于一个接口,然后在MEF创建对象和解析接口/依赖关系后,我的代码是否执行?所以,假设我有一个这样的类:

        public class MyClass 
      {
      [Import(ILogger )]
      public ILogger logger;

      public MyClass()
      {
      }

      public void DoSomething()
      {
      logger.Info(Hello世界!);
      }
      }

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



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


      I am interested in learning more about how people inject logging with dependency injection platforms. Although the links below and my examples refer to log4net and Unity, I am not necessarily going to use either of those. For dependency injection/IOC, I will probably use MEF as that is the standard that the rest of the project (large) is settling on.

      I am very new to dependency injection/ioc and am pretty new to C# and .NET (have written very little production code in C#/.NET after the past 10 years or so of VC6 and VB6). I have done a lot of investigation into the various logging solutions that are out there, so I think that I have a decent handle on their feature sets. I am just not familiar enough the with actual mechanics of getting one dependency injected (or, maybe more "correctly", getting an abstracted version of one dependency injected).

      I have seen other posts related to logging and/or dependency injection like: dependency injection and logging interfaces

      Logging best practices

      What would a Log4Net Wrapper class look like?

      again about log4net and Unity IOC config

      My question does not have specifically to do with "How to I inject logging platform xxx using ioc tool yyy?" Rather, I am interested in how people have handled wrapping the logging platform (as is often, but not always recommended) and configuration (i.e. app.config). For example, using log4net as an example, I could configure (in app.config) a number of loggers and then get those loggers (without dependency injection) in the standard way of using code like this:

      private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
      

      Alternatively, if my logger is not named for a class, but rather, for a functional area, I could do this:

      private static readonly ILog logger = LogManager.GetLogger("Login");
      private static readonly ILog logger = LogManager.GetLogger("Query");
      private static readonly ILog logger = LogManager.GetLogger("Report");
      

      So, I guess that my "requirements" would be something like this:

      1. I would like to insulate my product's source from a direct dependency on a logging platform.

      2. I would like to be able to resolve a specific named logger instance (probably sharing the same instance among all requesters of the same named instance) either directly or indirectly by some kind of dependency injection, probably MEF.

      3. I don't know if I would call this a hard requirement, but I would like the ability to get a named logger (different than the class logger) on demand. For example, I might create a logger for my class based on the class name, but one method needs particulary heavy diagnostics that I would like to control separately. In other words, I might want a single class to "depend" on two separate logger instances.

      Let's start with number 1. I have read a number of articles, primarily here on stackoverflow, about whether or not it is a good idea to wrap. See the "best practices" link above and go to jeffrey hantin's comment for one view about why it is bad to wrap log4net. If you did wrap (and if you could wrap effectively) would you wrap strictly for the purpose of injection/removal of direct depdency? Or would you also try to abstract away some or all of the log4net app.config information?

      Let's say I want to use System.Diagnostics, I would probably want to implement an interface-based logger (maybe even using the "common" ILogger/ILog interface), probably based on TraceSource, so that I could inject it. Would you implement the interface, say over TraceSource, and just use the System.Diagnostics app.config information as is?

      Something like this:

      public class MyLogger : ILogger
      {
        private TraceSource ts;
        public MyLogger(string name)
        {
          ts = new TraceSource(name);
        }
      
        public void ILogger.Log(string msg)
        {
          ts.TraceEvent(msg);
        }
      }
      

      And use it like this:

      private static readonly ILogger logger = new MyLogger("stackoverflow");
      logger.Info("Hello world!")
      

      Moving on to number 2 ... How to resolve a particular named logger instance? Should I just leverage the app.config information of the logging platform that I choose (i.e. resolve the loggers based on the naming scheme in the app.config)? So, in the case of log4net, might I prefer to "inject" LogManager (note that I know this is not possible since it is a static object)? I could wrap LogManager (call it MyLogManager), give it an ILogManager interface, and then resolve MyLogManager.ILogManager interface. My other objects could have a depenency (Import in MEF parlance) on ILogManager (Export from the assembly where it is implemented). Now I could have objects like this:

      public class MyClass
      {
        private ILogger logger;
        public MyClass([Import(typeof(ILogManager))] logManager)
        {
          logger = logManager.GetLogger("MyClass");
        }
      }
      

      Any time ILogManager is called, it would directly delegate to log4net's LogManager. Alternatively, could the wrapped LogManager take the ILogger instances that it gets based on the app.config and add them to the(a ?) MEF container by name. Later, when a logger of the same name is requested, the wrapped LogManager is queried for that name. If the ILogger is there, it is resolved that way. If this is possible with MEF, is there any benefit do doing so?

      In this case, really, only ILogManager is "injected" and it can hand out ILogger instances in the way that log4net normally does. How does this type of injection (essentially of a factory) compare to injecting the named logger instances? This does allow for more easy leveraging of log4net's (or other logging platform) app.config file.

      I know that I can get named instances out of the MEF container like this:

      var container = new CompositionContainer(<catalogs and other stuff>);
      ILogger logger = container.GetExportedValue<ILogger>("ThisLogger");
      

      But how do I get the named instances into the container? I know about the attribute based model where I could have different implementations of ILogger, each of which is named (via a MEF attribute), but that doesn't really help me. Is there a way to create something like an app.config (or a section therein) that would list the loggers (all of the same implementation) by name and that MEF could read? Could/should there be a central "manager" (like MyLogManager) that resolves named loggers via the underlying app.config and then inserts the resolved logger into the MEF container? This way it would be available to someone else that has access to the same MEF container (although without the MyLogManager's knowledge of how to use log4net's app.config information, it seems that the container would be unable to resolve any named loggers directly).

      This has already gotten pretty long. I hope it that it is coherent. Please feel free to share any specific information about how you dependency injected a logging platform (we are most likely considering log4net, NLog, or something (hopefully thin) built on System.Diagnostics) into your application.

      Did you inject the "manager" and have it return logger instances?

      Did you add some of your own config information in your own config section or in your DI platform's config section to make it easier/possible to inject logger instances directly (i.e. make your dependencies be on ILogger rather than ILogManager).

      What about having a static or global container that has either the ILogManager interface in it or the set of named ILogger instances in it. So, rather than injecting in the conventional sense (via constructor, property, or member data), the logging dependency is explicitly resolved on demand. Is this a good or bad way to dependency inject.

      I am marking this as a community wiki since it doesn't seem like a question with a definite answer. If anyone feels otherwise, feel free to change it.

      Thanks for any help!

      解决方案

      This is for the benefit of anyone that is trying to figure out how to inject a logger dependency when the logger that you want to inject is provided a logging platform such as log4net or NLog. My problem was that I could not understand how I could make a class (e.g. MyClass) dependent on an ILogger-type interface when I knew that the resolution of the specific ILogger would depend on knowing the type of the class that is dependent on ILogger (e.g. MyClass). How does the DI/IoC platform/container get the right ILogger?

      Well, I have looked at the source for Castle and NInject and have seen how they work. I have also looked AutoFac and StructureMap.

      Castle and NInject both provide an implementation of logging. Both support log4net and NLog. Castle also supports System.Diagnostics. In both cases, when the platform resolves the dependencies for a given object (e.g. when the platform is creating MyClass and MyClass depends on ILogger) it delegates the creation of the dependency (ILogger) to the ILogger "provider" (resolver might be a more common term). The implementation of the ILogger provider is then responsible for actually instantiating an instance of ILogger and handing it back out, to then be injected into the dependent class (e.g. MyClass). In both cases the provider/resolver knows the type of the dependent class (e.g. MyClass). So, when MyClass has been created and its dependencies are being resolved, the ILogger "resolver" knows that the class is MyClass. In the case of using the Castle or NInject provided logging solutions, that means that the logging solution (implemented as a wrapper over log4net or NLog) gets the type (MyClass), so it can delegate down to log4net.LogManager.GetLogger() or NLog.LogManager.GetLogger(). (Not 100% sure of syntax for log4net and NLog, but you get the idea).

      While AutoFac and StructureMap do not provide a logging facility (at least that I could tell by looking), they do seem to provide the ability to implement custom resolvers. So, if you wanted to write your own logging abstraction layer, you could also write a corresponding custom resolver. That way, when the container wants to resolve ILogger, your resolver would be used to get the correct ILogger AND it would have access to the current context (i.e. what object's dependencies are currently being satisfied - what object is dependent on ILogger). Get the type of the object, and you are ready to delegate the creation of the ILogger to the currently configured logging platform (that you have probably abstracted behind an interface and for which you have written a resolver).

      So, a couple of key points which I suspected were required but that I did not fully grasp before are:

      1. Ultimately the DI container must be aware, somehow, of what logging platform to use. Typically this is done by specifying that "ILogger" is to be resolved by a "resolver" that is specific to a logging platform (hence, Castle has log4net, NLog, and System.Diagnostics "resolvers" (among others)). The specification of which resolver to use can be done via config file or programmatically.

      2. The resolver needs to know the context for which the dependency (ILogger) is being resolved. That is, if MyClass has been created and it is dependent on ILogger, then when the resolver is trying to create the correct ILogger, it (the resolver) must know the current type (MyClass). That way, the resolver can use the underlying logging implementation (log4net, NLog, etc) to get the correct logger.

      These points might be obvious to those DI/IoC users out there, but I am just now coming into it, so it has taken me a while to get my head around it.

      One thing that I have not figured out yet is how or if something like this is possible with MEF. Can I have an object dependent on an interface and then have my code execute after MEF has created the object and while the interface/dependency is being resolved? So, assume I have a class like this:

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

      When MEF is resolving the imports for MyClass, can I have some of my own code (via an attribute, via an extra interface on the implementation of ILogger, elsewhere???) execute and resolve the ILogger import based on the fact that it is MyClass that is currently in context and give back a (potentially) different ILogger instance than would be retrieved for YourClass? Do I implement some sort of MEF Provider?

      At this point, I still don't know about MEF.

      这篇关于依赖注入和命名记录器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆