6

È possibile ottenere il contesto in modo da poter recuperare loggerName e utilizzare LogManager.GetLogger(loggerName) anziché LogManager.GetCurrentClassLogger()?Registrazione di NLog ILogger con iniettore semplice

Ho notato che container.RegisterConditional() ha accesso al contesto.

Inoltre, voglio evitare soluzioni come SimpleLogging.NLog per ora.

Infine, sono disposto ad accettare che questo non sia l'approccio giusto. BTW, AOP è un'opzione che ho già esplorato (Is it a good practice to have logger as a singleton?).

Nota: sono consapevole che GetCurrentClassLogger() ottiene le stesse informazioni che avrei recuperato con .NET reflection.

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

cosa vorresti fosse il valore di 'loggerName'? – qujck

+1

Correlati: https://stackoverflow.com/q/32952701/264697 – Steven

+1

L'applicazione delle tecniche AOP è molto utile, ma si prega di stare lontano dagli strumenti di tessitura del codice. Applicare AOP utilizzando decoratori. Correlato: https://stackoverflow.com/a/9915056/264697 – Steven

risposta

7

È necessario definire un programma di registrazione proxy che instradi il messaggio al Nlog Logger corretto. Questo proxy è piuttosto semplice:

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

È possibile registrare questo come

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

ovunque bisogna accedere solamente dovrà iniettare ILogger.

Come per AOP. Non sono sicuro di cosa intendi con questo commento

Un wrapper che deve essere mantenuto (il contratto di NLog.ILogger è enorme).

Logging è un cross cutting concern e utilizzando un decorator è un ottimo modo per applicare preoccupazioni trasversali. Con un decoratore non sarà possibile accedere a tutte le chiamate (private) di ingresso e uscita della chiamata, ma perché lo vuoi? Come puoi leggere here probabilmente non ne hai bisogno. Il semplice fatto che venga chiamato un servizio (con i dati passati a questo servizio) e le eventuali eccezioni vengono registrate con lo stacktrace completo sarà più che sufficiente nella maggior parte dei casi.

Quindi considerare questo:

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

Questo decoratore registrerà tutte le chiamate di funzione con le informazioni passate al servizio e anche registrare alcuna eccezione.

Ma questo approccio aumenterebbe il numero di classi per 2, quindi non molto DRY. Questo problema è causato dal fatto che questo design è almeno subottimale. L'utilizzo di un progetto attorno a un'unica astrazione generica aperta risolverà completamente questo problema. È possibile leggere su questo design here e here.

In questo caso si avrebbe un unico `LoggingDecorator' come

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

E questo singolo decoratore registrerà tutti i comandi.

Questa è la mia visione di AOP ....

+1

@ ric-net Penso che la tua risposta non riguardi solo i commenti di Steven, ma sottolinea anche la presa di Jeff Atwood sulla registrazione: [The Problem With Logging] (http://blog.codinghorror.com/the-problem-with-logging/). Una discussione interessante, vecchia ma buona, che qualsiasi architetto deve affrontare. –

+0

Davvero utile, grazie – james