Регистрация NLog ILogger с помощью простого инжектора

Есть ли способ получить контекст, чтобы я мог получить loggerName и использовать LogManager.GetLogger(loggerName) вместо LogManager.GetCurrentClassLogger()?

Я заметил, что container.RegisterConditional() имеет доступ к контексту.

Также я хочу пока избегать таких решений, как SimpleLogging.NLog.

Наконец, я готов признать, что это не правильный подход. Кстати, AOP - это вариант, который я уже исследовал (Это хорошая практика - использовать регистратор в качестве синглтона?).

Примечание. Мне известно, что GetCurrentClassLogger() получает ту же информацию, которую я получал бы с помощью отражения .NET.

using NLog;
using SimpleInjector;

namespace DependencyInjection
{
    class Program
    {
        private static Container _container;
        static void Main(string[] args)
        {
            Bootstrap();
            _container.GetInstance<Greeter>().Greet();
        }

        private static void Bootstrap()
        {
            _container = new Container();

            _container.Register<ILogger>(() => LogManager.GetCurrentClassLogger(), Lifestyle.Transient);
            _container.Register<Greeter>();

            _container.Verify();
        }

        public class Greeter
        {
            private ILogger _logger;

            public Greeter(ILogger logger)
            {
                _logger = logger;
            }

            public void Greet()
            {
                _logger.Log(LogLevel.Info, "Hello world!");
            }
        }
    }
}

Ответ 1

Вам нужно определить прокси-регистратор, который направляет сообщение правильному Nlog Logger. Этот прокси-сервер довольно прост:

public class NLogProxy<T> : ILogger
{
    private static readonly NLog.ILogger logger = 
                 LogManager.GetLogger(typeof (T).FullName);

    void ILogger.Log(string message)
    {
        logger.Log(LogLevel.Info, message);
    }
}

Вы можете зарегистрировать это как

container.RegisterConditional(typeof(ILogger), 
     context => typeof(NLogProxy<>).MakeGenericType(context.Consumer.ImplementationType), 
     Lifestyle.Singleton, context => true);

Всюду, где вам нужно вести журнал, вам нужно будет вводить ILogger.

Что касается АОП. Я не уверен, что вы подразумеваете под этим комментарием.

Обертка, которая должна поддерживаться (контракт NLog.ILogger огромен).

Ведение журнала - это проблема перекрестных связей и использование decorator - отличный способ применить проблемы перекрестной резки. С помощью декоратора невозможно будет зарегистрировать каждый (закрытый) вход и выход вызова функции, но зачем вам это нужно? Как вы можете прочитать здесь, вам, вероятно, это не нужно. Простой факт, что услуга вызывается (с данными, переданными этой службе) и возможными исключениями, регистрируется с полным стеком, будет в большинстве случаев более чем достаточным.

Поэтому рассмотрим следующее:

public interface ISomeService
{
    void DoSomething(string someParameter);
}

public class SomeServiceDecorator : ISomeService
{
    private readonly ISomeService decoratee;
    private readonly ILogger logger;

    public SomeServiceDecorator(ISomeService decoratee, ILogger logger)
    {
        this.decoratee = decoratee;
        this.logger = logger;
    }

    public void DoSomething(string someParameter)
    {
        try
        {
            this.logger.Log(string.Format("Do something called with {0}", someParameter));
            this.decoratee.DoSomething(someParameter);
        }
        catch (Exception e)
        {
            this.logger.Log(e.ToString());                
            throw;
        }
    }
}

Этот декоратор будет записывать все вызовы функций с информацией, переданной службе, а также регистрирует любое исключение.

Но этот подход увеличил бы количество классов на 2, поэтому не очень DRY. Эта проблема вызвана тем, что этот проект по крайней мере субоптимален. Использование дизайна вокруг одной открытой общей абстракции полностью решит эту проблему. Вы можете прочитать об этом проекте здесь и здесь.

В этом случае у вас будет один "LoggingDecorator" как

public class LoggingCommandHandlerDecorator<T> : ICommandHandler<T>
{
    private readonly ICommandHandler<T> decoratee;
    private readonly ILogger logger;

    public LoggingCommandHandlerDecorator(ICommandHandler<T> decoratee, ILogger logger)
    {
        this.decoratee = decoratee;
        this.logger = logger;
    }

    public void Handle(T command)
    {
        // serialize command to json and log
        this.logger.Log(serializedcommandData);
        this.decoratee.Handle(command);
    }
}

И этот единственный декоратор будет записывать все ваши команды.

Это мое видение АОП....