Registering NLog ILogger with Simple Injector

前端 未结 1 2009
悲&欢浪女
悲&欢浪女 2021-02-07 13:43

Is there any way I can get the context so I can retrieve the loggerName and use LogManager.GetLogger(loggerName) instead of LogManager.GetCurrent

相关标签:
1条回答
  • 2021-02-07 14:11

    You need to define a proxy logger which routes the message to the correct Nlog Logger. This proxy is pretty simple:

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

    You can register this as

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

    Everywhere you need logging you only will have to inject ILogger.

    As for AOP. I'm unsure what you mean by this comment

    A wrapper that has to be maintained (NLog.ILogger contract is huge).

    Logging is a cross cutting concern and using a decorator is a great way to apply cross cutting concerns. With a decorator it won't be possible to log every (private) function call entrance and exit but why would you want that? As you can read here you probably don't need that. The simple fact that a service is called (with the data passed to this service) and possible exceptions are logged with the complete stacktrace will be more than sufficient in most cases.

    So consider this:

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

    This decorator will log all function calls with the information passed to the service and will also log any exception.

    But this approach would increase the number of classes by 2, so not very DRY. This problem is caused because this design is at least suboptimal. Using a design around a single open generic abstraction will totally solve this problem. You can read about this design here and here.

    In this case you would have a single `LoggingDecorator' as

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

    And this single decorator will log all your commands.

    That's my vision of AOP....

    0 讨论(0)
提交回复
热议问题