Использование 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 должен создать экземпляр ILog с именем "HomeController"

Вот что у меня для config

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, не то, что я ожидаю. Я не могу понять ни рифмы, ни причины, иногда это правильно, но в большинстве случаев это не так. Имена, которые я вижу, это имена ДРУГИХ классов, которые также имеют зависимости от ILogger.

Ответ 1

Расширение Ninject.Extension.Logging уже предоставляет все, что вы реализуете самостоятельно. Включая поддержку log4net, NLog и NLog2.

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


Также вы хотите использовать следующие типы регистратора:

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

В противном случае вы получите регистратор для типа службы вместо типа реализации.

Ответ 2

Я лично не заинтересован в абстрагировании моего регистратора, поэтому мои модули реализации ссылаются на log4net.dll напрямую, а мои конструкторы запрашивают ILog по желанию.

Для этого однострочная регистрация с использованием Ninject v3 выглядит так в конце моего static void RegisterServices( IKernel kernel ):

        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" );
        }
    }

Для облегчения диагностики проблем с протоколированием я вначале прикладываю следующее сообщение:

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

Ответ 3

Объем ILog и ILogger должен быть временным, иначе он просто повторно использует первый созданный журнал. Спасибо @Meryln Morgan-Graham за то, что помогли мне найти это.

Ответ 4

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

В настоящее время вы привязываетесь к области Singleton, поэтому создается только один регистратор, который будет использовать имя созданного первого. Вместо этого используйте InTransientScope()

Ответ 5

Возможно, мой ответ задерживается, но я использую этот формат:

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

Ответ 6

Для всех вас, которые все еще ищут правильный ответ, правильная реализация:

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

Ответ 7

Мне нравится идея переноса Log4Net в мои собственные интерфейсы. Я не хочу быть зависимым от реализации Ninjects, потому что для меня это просто означает, что я беру зависимость от Ninject во всем моем приложении, и я думал, что это была полная противоположность тому, для чего предназначена инъекция зависимостей. Отделить от сторонних служб. Поэтому я взял исходный код плакатов, но я изменил следующий код, чтобы он работал.

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

Мне нужно вызвать ParentRequest.ParentRequest, чтобы при печати макета% logger он напечатал класс, который вызывает метод журнала Log4Net, вместо класса Log4Net метода, который вызвал метод журнала.