使用Ninject填充Log4Net依赖关系

本文关键字:依赖 关系 Log4Net 填充 Ninject 使用 | 更新日期: 2023-09-27 18:03:08

我在应用程序中将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<ILog>().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x)))
                .InSingletonScope();
            Bind<ILogger>().To<Log4NetLogger>()
                .InSingletonScope();
        }
        private string GetParentTypeName(IContext context)
        {
            return context.Request.ParentContext.Request.ParentContext.Request.Service.FullName;
        }
    }

然而,传递给ILog的"Name"并不是我所期望的。我也找不出任何韵律或理由,有时是对的,大多数时候不是。我看到的名称是OTHER类的名称,它们也依赖于ILogger。

使用Ninject填充Log4Net依赖关系

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

为了实现这一点,在我的static void RegisterServices( IKernel kernel ):末尾,使用Ninjectv3的一行注册如下所示

        kernel.Bind<ILog>().ToMethod( context=> 
            LogManager.GetLogger( context.Request.Target.Member.ReflectedType ) );
        kernel.Get<LogCanary>();
    }
    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" );

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

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

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

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


您还想使用以下作为记录器类型:

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

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

ILogILogger的作用域必须是瞬态的,否则它将只重用它创建的第一个记录器。感谢@Meryln Morgan Graham帮我找到了它。

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

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

也许我的答案迟到了,但我使用的是以下格式:

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

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

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

重点:

x.Request.Target.Member.DeclaringType

我非常喜欢在自己的接口中封装Log4Net的想法。我不想依赖Ninject的实现,因为对我来说,这意味着我在整个应用程序中都依赖Ninject,我认为这与依赖注入的目的完全相反。与第三方服务分离。所以我采用了最初的海报代码,但我更改了以下代码以使其发挥作用。

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

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