2014-05-13 58 views
3

我搜索了很长一段时间,不幸的是,当我搜索涉及Unity或IoC与Log4Net的任何事情时,我得到的唯一结果是如何使ILog通过IoC自动填充。这是不是我正在尝试做什么。在自定义Log4Net appender中使用IoC

我有一个自定义Appender,通过WCF传输数据到服务器。我想要做的就是传递一个工厂方法,最好是由Unity生成的方法,它为我创建了WCF客户端类,这样我就可以在单元测试期间将实际客户端类替换为存根。问题是我无法找到关于如何将参数传递给自定义log4net appender的任何解释。

这是我通常会如何使用Unity实现这个。

public class WcfAppender : BufferingAppenderSkeleton 
{ 
    public WcfAppender(Func<ClientLoggerClient> loggerClientFactory) //How do I get this Func passed in? 
    { 
     LoggerClientFactory = loggerClientFactory; 
    } 


    private readonly Func<ClientLoggerClient> LoggerClientFactory; 
    private static readonly ILog Logger = LogManager.GetLogger(typeof(WcfAppender)); 
    private static readonly string LoggerName = typeof(WcfAppender).FullName; 

    public override void ActivateOptions() 
    { 
     base.ActivateOptions(); 
     this.Fix = FixFlags.All; 
    } 

    protected override bool FilterEvent(LoggingEvent loggingEvent) 
    { 
     if (loggingEvent.LoggerName.Equals(LoggerName)) 
      return false; 
     else 
      return base.FilterEvent(loggingEvent); 
    } 


    protected override void SendBuffer(LoggingEvent[] events) 
    { 
     try 
     { 
      var client = LoggerClientFactory(); 
      try 
      { 
       client.LogRecord(events.Select(CreateWrapper).ToArray()); 
      } 
      finally 
      { 
       client.CloseConnection(); 
      } 
     } 
     catch (Exception ex) 
     { 
      Logger.Error("Error sending error log to server", ex); 
     } 
    } 

    private ErrorMessageWrapper CreateWrapper(LoggingEvent arg) 
    { 
     var wrapper = new ErrorMessageWrapper(); 

     //(Snip) 

     return wrapper; 
    } 
} 

但是我不叫container.Resolve<WcfAppender>()这是在new WcfAppender()被称为log4net的库。如何告诉log4net库使用new WcfAppender(factoryGeneratedFromUnity)

回答

2

我能够在创建后运行,通过统一容器的appender实例:

ILog logger = LogManager.GetLogger("My Logger"); 
IAppender[] appenders = logger.Logger.Repository.GetAppenders(); 
foreach (IAppender appender in appenders) 
{ 
    container.BuildUp(appender.GetType(), appender); 
} 

container.RegisterInstance(logger); 

BuildUp()方法将填充在附加器类的注入性质:

public class LogDatabaseSaverAppender : AppenderSkeleton 
{ 
    [Dependency] 
    public IContextCreator ContextCreator { get; set; } 

    ... 
} 
2

从我的,追加程序是由XmlHierarchyConfigurator类创建的代码中看到,在ParseAppender方法:

protected IAppender ParseAppender(XmlElement appenderElement) 
{ 
    string attribute = appenderElement.GetAttribute("name"); 
    string attribute2 = appenderElement.GetAttribute("type"); 
    // <snip> 
    try 
    { 
     IAppender appender = (IAppender)Activator.CreateInstance(SystemInfo.GetTypeFromString(attribute2, true, true)); 
     appender.Name = attribute; 
     // <snip> 

的方法完成通过调用IOptionHandler接口实现的方法加载的appender(你已经有它在你的appender,因为你在你的祖先树有AppenderSkeleton类)

IOptionHandler optionHandler = appender as IOptionHandler; 
if (optionHandler != null) 
{ 
    optionHandler.ActivateOptions(); 
} 

不是由log4net的任何已知的XML参数推到一个属性与 一样的名字。因此,有可能从XML配置文件中完全配置的appender并启动你需要有你的appender任何具体行为实施IOptionHandler

的唯一途径(巴重建一个自定义log4net的),你需要一个Func<ClientLoggerClient>传递给您的appender是使用一个静态事件,你appender将在ActivateOptions()方法中调用,这个事件将被你选择的解析方法拾取;该事件可以包含来自appender的xml配置的一些自定义,因此您可以根据该配置路由分辨率。让活动将足够的Func<ClientLoggerClient>回馈给你的appender,并且你很好。

+0

相反挂钩的在静态事件中,我使用log4net的'ILoggerRepository.Properties'系统来存储委托。详情请参阅我的回答。 –

3

我认为samy is right,这里是我如何解决它的实现。

ActivateOptions()在我初始化Unity容器之前被调用,所以为了安全起见,我只是把工厂方法的检索放在SendBuffer方法中。我结束了使用LogManager.GetRepository().Properties属性来存储工厂对象。

public class WcfAppender : BufferingAppenderSkeleton 
{ 
    private static readonly ILog Logger = LogManager.GetLogger(typeof(WcfAppender)); 
    private static readonly string LoggerName = typeof(WcfAppender).FullName; 

    public override void ActivateOptions() 
    { 
     base.ActivateOptions(); 
     this.Fix = FixFlags.All; 
    } 

    protected override bool FilterEvent(LoggingEvent loggingEvent) 
    { 
     if (loggingEvent.LoggerName.Equals(LoggerName)) 
      return false; 
     else 
      return base.FilterEvent(loggingEvent); 
    } 


    protected override void SendBuffer(LoggingEvent[] events) 
    { 
     try 
     { 
      var clientFactory = log4net.LogManager.GetRepository().Properties["ClientLoggerFactory"] as Func<ClientLoggerClient>; 
      if (clientFactory != null) 
      { 
       var client = clientFactory(); 
       try 
       { 
        client.LogRecord(events.Select(CreateWrapper).ToArray()); 
       } 
       finally 
       { 
        client.CloseConnection(); 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      Logger.Error("Error sending error log to server", ex); 
     } 
    } 

    private ErrorMessageWrapper CreateWrapper(LoggingEvent arg) 
    { 
     var wrapper = new ErrorMessageWrapper(); 

     //SNIP... 

     return wrapper; 
    } 

} 

然后创建工厂并在启动程序时存储它。

static class Program 
{ 
    private static readonly ILog Logger = LogManager.GetLogger(typeof(Program)); 

    static void Main(string[] args) 
    { 
     log4net.Util.SystemInfo.NullText = String.Empty; 

     AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 

     Logger.Debug("Service Starting"); 
     using (var container = new UnityContainer()) 
     { 
      var debugMode = args.Contains("--debug", StringComparer.InvariantCultureIgnoreCase); 

      BaseUnityConfiguration.Configure(container, debugMode); 
      LogManager.GetRepository().Properties["ClientLoggerFactory"] = container.Resolve<Func<ClientLoggerClient>>(); 

      //...