2016-10-07 2 views
2

Ich konfiguriere Protokollierung für meine Anwendung und für die Protokollierung verwende ich log4net und castle windsor für DI.Log4Net mit Schloss windsor

Ich möchte Protokollierungsrahmen in benutzerdefinierten Implementierung Wrap, so dass es in Zukunft geändert werden kann.

public interface ICustomLogger 
{ 
    void Debug(object message, Exception ex = null); 
    void Info(object message, Exception ex = null); 
    void Warn(object message, Exception ex = null); 
    void Error(object message, Exception ex = null); 
    void Fatal(object message, Exception ex = null); 
} 

public class CustomLogger : ICustomLogger 
{ 
    private readonly log4net.ILog _log; 
    private readonly log4net.ILog _log1; 

    public CustomLogger() 
    { 
     //approach1 
     var stack = new StackTrace(); 
     var frame = stack.GetFrame(1); 
     var method = frame.GetMethod(); 
     Type type = method.DeclaringType; 
     _log = log4net.LogManager.GetLogger(type); 

     //approach2 
     var dtype = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType; 
     _log1 = log4net.LogManager.GetLogger(dtype); 
    } 

    public CustomLogger(string name) 
    { 
     _log = log4net.LogManager.GetLogger(name); 
    } 

    public CustomLogger(Type type) 
    { 
     _log = log4net.LogManager.GetLogger(type); 
    } 

    public void Debug(object message, Exception ex = null) 
    { 
     if (_log.IsDebugEnabled) 
     { 
      if (ex == null) 
      { 
       _log.Debug(message); 
      } 
      else 
      { 
       _log.Debug(message, ex); 
      } 
     } 
    } 

    public void Info(object message, Exception ex = null) 
    { 
     if (_log.IsInfoEnabled) 
     { 
      if (ex == null) 
      { 
       _log.Info(message); 
      } 
      else 
      { 
       _log.Info(message, ex); 
      } 
     } 
    } 

    public void Warn(object message, Exception ex = null) 
    { 
     if (_log.IsWarnEnabled) 
     { 
      if (ex == null) 
      { 
       _log.Warn(message); 
      } 
      else 
      { 
       _log.Warn(message, ex); 
      } 
     } 
    } 

    public void Error(object message, Exception ex = null) 
    { 
     if (_log.IsErrorEnabled) 
     { 
      if (ex == null) 
      { 
       _log.Error(message); 
      } 
      else 
      { 
       _log.Error(message, ex); 
      } 
     } 
    } 

    public void Fatal(object message, Exception ex = null) 
    { 
     if (_log.IsFatalEnabled) 
     { 
      if (ex == null) 
      { 
       _log.Fatal(message); 
      } 
      else 
      { 
       _log.Fatal(message, ex); 
      } 
     } 
    } 
} 

Um diese benutzerdefinierte Implementierung mit DI zu registrieren ...

container.Register(Component.For<ICustomLogger>() 
            .ImplementedBy<CustomLogger>() 
            .LifeStyle.Transient); 

Problem kommt, wenn ich DI fragen Logger zu lösen, ist es immer dann Logger für Customlogger Typ zurückgeben nicht die Klasse, wo ich verwenden möchte es.

class ABC 
{ 
    ICustomLogger _logger; 

    public ABC(ICustomLogger logger) 
    { 
     _logger = logger; // type of this logger is CustomLogger not ABC 
    } 
} 

Beide Ansätze funktionieren nicht, um Logger als ABC aufzulösen. Kann mir jemand helfen zu verstehen, was hier falsch ist und wie man das Problem beheben kann.

Antwort

2

Sie können dies über eine benutzerdefinierte dependency resolver tun.

Als erstes müssen Sie eine Implementierung von ISubDependencyResolver erstellen, die ICustomLogger Abhängigkeiten vom Typ auflösen kann:

public class LoggerResolver : ISubDependencyResolver 
{ 
    public bool CanResolve(
     CreationContext context, 
     ISubDependencyResolver contextHandlerResolver, 
     ComponentModel model, 
     DependencyModel dependency) 
    { 
     //We can only handle dependencies of type ICustomLogger 
     return dependency.TargetType == typeof (ICustomLogger); 
    } 

    public object Resolve(
     CreationContext context, 
     ISubDependencyResolver contextHandlerResolver, 
     ComponentModel model, 
     DependencyModel dependency) 
    { 
     //We pass the requested type, e.g. ABC, to the constructor of CustomLogger 
     return new CustomLogger(context.RequestedType); 
    } 
} 

Sie dann mit dem Behälter wie diese diese Resolver registrieren müssen:

container.Kernel.Resolver.AddSubResolver(new LoggerResolver()); 
1

Für Ihre spezifische Frage - in beiden Ansätzen verlässt man nie den "Umfang" Ihrer Klasse. Mit dem ersten erstellen Sie ein neues StackTrace und in dem anderen ist der deklarierende Typ eines Konstruktors die Klasse selbst.

Sie haben jedoch einen Konstruktor implementiert, der einen Typ empfangen kann, also warum nicht verwenden. Zur Zeit ist Ihr CustomLogger mit Ihrem Standard-Konstruktor registriert:

//There is no place here that you tell castle to resolve using the constructor 
//that receives `ABS` 
container.Register(Component.For<ICustomLogger>() 
          .ImplementedBy<CustomLogger>() 
          .LifeStyle.Transient); 

Castle Windsor passing constructor parameters Siehe zu verstehen, wie die Parameter zu übergeben und auf diese Weise den Konstruktor aufrufen möchten Sie


Zusätzlich - Worth Umdenken :

Obwohl es eine gute Idee ist, eine solche Abstraktion zwischen Ihrem Code und externen Quellen zu erstellen in diesem Fall würde ich es nicht tun und ich werde erklären warum:

  1. Aus meiner Erfahrung ändert man nicht wirklich das Protokollierungs-Framework, nachdem der Code läuft. Zumal Sie mit einem ausgereiften und exzellenten Framework arbeiten - Log4Net. Es hat viele eingebaute Fähigkeiten und ist sehr anpassungsfähig an seine Bedürfnisse: Von der unterschiedlichen Formatierung der Nachricht bis zur Ausgabe der Logs zu verschiedenen Quellen wie Datenbanken, Dateien und wenn ich mich nicht irre, gibt es auch Appender für Dinge wie elastische Suche.
  2. Sie verwenden Castle Windsor, die eine gute Integration mit Log4Net hat und für Sie ein fertiges zu Log4Net hat. Siehe this question für wie einfach es ist, es hinzuzufügen.
  3. Letzter Punkt ist, dass, wenn Sie bereits gut SOLID Code schreiben und Ihre Logger als ILogger auf alle Komponenten (und nicht eine spezifische Implementierung) passieren alle sie werden wahrscheinlich Debug/Info/Warn/Error/Fatal Methoden der verschiedenen tun ist, rufen - welche andere ausgereifte Protokollierungsumgebung haben wird. An dem Tag, an dem Sie Änderungen vornehmen müssen (was nicht passieren wird), können Sie eine Schnittstelle schreiben, die wie die Schnittstelle von Log4Net aussieht, und eine Implementierung, die diese an Ihr neues Protokollierungsframework anpasst.
Verwandte Themen