依赖注入和命名记录器

时间:2010-08-10 18:48:14

标签: c# logging dependency-injection log4net nlog

5 个答案:

答案 0 :(得分:37)

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

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

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

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

我猜这种方法也适用于其他IOC容器。

答案 1 :(得分:15)

也可以使用Common.Logging外观或Simple Logging Facade

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

坦率地说,日志记录是我在自动注入时看不到任何价值的依赖项之一。

我需要日志记录服务的大多数类都是这样的:

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

}

通过使用Simple Logging Facade,我已经将一个项目从log4net切换到NLog,除了我的应用程序使用NLog进行日志记录之外,我还添加了使用log4net的第三方库的日志记录。也就是说,立面对我们有好处。

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

答案 2 :(得分:13)

当您要注入的记录器被提供日志记录平台(如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“提供者”(解析器可能更多)常用术语)。然后,ILogger提供程序的实现负责实际实例化ILogger的实例并将其交还出来,然后将其注入依赖类(例如MyClass)。在这两种情况下,提供者/解析器都知道依赖类的类型(例如,MyClass)。因此,当MyClass被创建并且其依赖关系被解析时,ILogger“resolver”知道该类是MyClass。在使用Castle或NInject提供的日志记录解决方案的情况下,这意味着日志记录解决方案(作为log4net或NLog上的包装器实现)获取类型(MyClass),因此它可以委派给log4net.LogManager.GetLogger()或NLog.LogManager.GetLogger()。 (不是100%确定log4net和NLog的语法,但是你明白了。)

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

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

  1. 最终DI容器必须是 不知怎的,知道什么是伐木 平台使用。通常这是 通过指定“ILogger”来完成 由“解析者”解决 特定于日志记录平台 (因此,Castle有log4net,NLog, 和System.Diagnostics“解析器” (其中))。规范 可以使用哪个解析器 通过配置文件或以编程方式。

  2. 解析器需要知道     依赖的上下文     (ILogger)正在解决。那     是,如果MyClass已经创建并且     那依赖于ILogger     当解析器试图     创造正确的ILogger,它(     解析器)必须知道当前的类型     (我的课)。那样,解析器     可以使用底层日志记录     实现(log4net,NLog等)     获得正确的记录器。

  3. 对于那些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。

答案 3 :(得分:5)

我看到你想出了自己的答案:)但是,对于未来有关于如何不将自己绑定到特定日志框架的问题的人们来说,这个库:Common.Logging可以帮助确切地解决这个问题。

答案 4 :(得分:0)

我做了我的自定义 ServiceExportProvider ,由提供者注册Log4Net记录器,以便MEF进行依赖注入。因此,您可以使用记录器进行不同类型的注射。

注射实例:

[Export]
public class Part
{
    [ImportingConstructor]
    public Part(ILog log)
    {
        Log = log;
    }

    public ILog Log { get; }
}

[Export(typeof(AnotherPart))]
public class AnotherPart
{
    [Import]
    public ILog Log { get; set; }
}

使用示例:

class Program
{
    static CompositionContainer CreateContainer()
    {
        var logFactoryProvider = new ServiceExportProvider<ILog>(LogManager.GetLogger);
        var catalog = new AssemblyCatalog(typeof(Program).Assembly);
        return new CompositionContainer(catalog, logFactoryProvider);
    }

    static void Main(string[] args)
    {
        log4net.Config.XmlConfigurator.Configure();
        var container = CreateContainer();
        var part = container.GetExport<Part>().Value;
        part.Log.Info("Hello, world! - 1");
        var anotherPart = container.GetExport<AnotherPart>().Value;
        anotherPart.Log.Fatal("Hello, world! - 2");
    }
}

控制台中的结果:

2016-11-21 13:55:16,152 INFO  Log4Mef.Part - Hello, world! - 1
2016-11-21 13:55:16,572 FATAL Log4Mef.AnotherPart - Hello, world! - 2

ServiceExportProvider 实施:

public class ServiceExportProvider<TContract> : ExportProvider
{
    private readonly Func<string, TContract> _factoryMethod;

    public ServiceExportProvider(Func<string, TContract> factoryMethod)
    {
        _factoryMethod = factoryMethod;
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
    {
        var cb = definition as ContractBasedImportDefinition;
        if (cb?.RequiredTypeIdentity == typeof(TContract).FullName)
        {
            var ce = definition as ICompositionElement;
            var displayName = ce?.Origin?.DisplayName;
            yield return new Export(definition.ContractName, () => _factoryMethod(displayName));
        }
    }
}