I have searched quite a while unfortunately when I search for anything involving Unity or IoC with Log4Net the only results I get is how to make ILog
automatically populated via IoC. This is not what I am trying to do.
I have a custom Appender that transmits data over WCF to a server. What I would like to do is pass in a factory method, preferably one generated by Unity, that creates the WCF client class for me so I can swap out the real client class for stubs during unit testing. The problem is I can not find any explanation anywhere on how to pass a argument in to a custom log4net appender.
Here is how I would normally implement this using 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;
}
}
However I don't call container.Resolve<WcfAppender>()
it is in the log4net library that new WcfAppender()
gets called. How do I tell the log4net library to use new WcfAppender(factoryGeneratedFromUnity)
instead?
From what i see in the code, appenders are created by the
XmlHierarchyConfigurator
class, in theParseAppender
method:The method finishes loading the appender by calling a method implemented by the
IOptionHandler
interface (you already have it in your appender, because you have theAppenderSkeleton
class in your ancestors tree)Any xml parameter that is not known by log4net is pushed to a property with the same name. So it is possible to configure your appender entirely from the xml config file and launch any specific behavior you need by having your appender implement
IOptionHandler
The only way (bar rebuilding a custom log4net) you have to pass a
Func<ClientLoggerClient>
to your appender is to use a static event that you appender will call in theActivateOptions()
method, event that would be picked up by your resolving method of choice; the event can contain some customization coming from the xml configuration for the appender, so you can route the resolution based on that. Let the event give back the adequateFunc<ClientLoggerClient>
to your appender and you are good to go.I was able to run the appender instance through Unity container after its creation:
BuildUp()
method will populate the injection property in the appender class:I think samy is right, here is the implementation of how I solved it.
ActivateOptions()
was being called before I was initializing my Unity container, so to play it safe I just put the retrieval of the factory method inside theSendBuffer
method. I ended up using theLogManager.GetRepository().Properties
property to store the factory object.I then create the factory and store it during the initiation of my program.