使用Ninject填充Log4Net依赖关系

我在我的应用程序中使用Ninject作为DI容器。 为了松散地耦合到我的日志库,我使用这样的接口:

public interface ILogger { void Debug(string message); void Debug(string message, Exception exception); void Debug(Exception exception); void Info(string message); ...you get the idea 

我的实现看起来像这样

 public class Log4NetLogger : ILogger { private ILog _log; public Log4NetLogger(ILog log) { _log = log; } public void Debug(string message) { _log.Debug(message); } ... etc etc 

具有日志记录依赖性的示例类

 public partial class HomeController { private ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } 

在实例化Log4Net的实例时,您应该为其提供要记录的类的名称。 这对Ninject来说是一个挑战。

目标是在实例化HomeController时,Ninject应该使用“HomeController”的“名称”实例化ILog

这是我的配置

 public class LoggingModule : NinjectModule { public override void Load() { Bind().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x))) .InSingletonScope(); Bind().To() .InSingletonScope(); } private string GetParentTypeName(IContext context) { return context.Request.ParentContext.Request.ParentContext.Request.Service.FullName; } } 

然而,传递给ILog的“名称”并不是我所期待的。 我也无法弄清楚任何押韵或理由,有时是正确的,大部分时间都不是。 我看到的名称是OTHER类的名称,它们也依赖于ILogger。

Ninject.Extension.Logging扩展已经提供了您自己实现的所有function。 包括对log4net,NLog和NLog2的支持。

https://github.com/ninject/ninject.extensions.logging


您还希望将以下内容用作记录器类型:

 context.Request.ParentRequest.ParentRequest.Target.Member.DeclaringType 

否则,您将获得服务类型的记录器而不是实现类型。

我个人没有兴趣抽象掉我的记录器,所以我的实现模块直接引用log4net.dll ,我的构造函数根据需要请求ILog

为实现这一点,使用Ninject v3的单行注册在我的static void RegisterServices( IKernel kernel )的末尾看起来像这样:

  kernel.Bind().ToMethod( context=> LogManager.GetLogger( context.Request.Target.Member.ReflectedType ) ); kernel.Get(); } class LogCanary { public LogCanary(ILog log) { log.Debug( "Debug Logging Canary message" ); log.Info( "Logging Canary message" ); } } 

为了便于诊断日志记录问题,我在开始时坚持以下内容来获取非DI驱动的消息:

 public static class NinjectWebCommon { public static void Start() { LogManager.GetLogger( typeof( NinjectWebCommon ) ).Info( "Start" ); 

在启动应用程序时产生以下结果:

  INFO MeApp.App_Start.NinjectWebCommon - Start  DEBUG MeApp.App_Start.NinjectWebCommon+LogCanary - Debug Logging Canary message  INFO MeApp.App_Start.NinjectWebCommon+LogCanary - Logging Canary message 

ILogILogger的范围需要是瞬态的,否则它将只重用它创建的第一个记录器。 感谢@Meryln Morgan-Graham帮我找到了。

 Bind().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x))) .InSingletonScope(); 

您当前在Singleton范围内绑定,因此只创建一个将使用创建的第一个记录器的记录器。 而是使用InTransientScope()

对于仍在寻找正确答案的所有人,正确的实施是:

 public class LoggingModule : NinjectModule { public override void Load() { Bind().ToMethod(x => LogManager.GetLogger(x.Request.Target.Member.DeclaringType)); Bind().To() .InSingletonScope(); } } 

重点是:

 x.Request.Target.Member.DeclaringType 

也许我的答案很晚,但我正在使用这种格式:

 private static void RegisterServices(IKernel kernel) { kernel.Bind() .ToMethod(c => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)) .InSingletonScope(); } 

我喜欢在我自己的界面中包装Log4Net的想法。 我不想依赖于Ninjects实现,因为对我而言,这只意味着我在整个应用程序中依赖于Ninject,我认为这与dependency injection的完全相反。 从第三方服务中解脱出来。 所以我采用了原始的海报代码,但我更改了以下代码以使其工作。

  private string GetParentTypeName(IContext context) { var res = context.Request.ParentRequest.ParentRequest.Service.FullName; return res.ToString(); } 

我必须调用ParentRequest.ParentRequest,这样当我打印布局%logger时,它将打印调用Log4Net日志方法的类,而不是调用Log方法的方法的Log4Net类。