log4net custom appender dependency

2.4k views Asked by At

I'm going to implement a log4net custom appender. My appender work fine but during refactory i want isolate the responsability, so i need to inject one interface. I write some code so you can understand

namespace WebServiceLogger.Interfaces
{
    public interface IClientService
    {
        void SendLog(string logToSend);
    }
}

This is my Appender that now send log by mail, but tomorrow i want use the same appender for send log to a web service.

using log4net.Appender;
using log4net.Core;
using WebServiceLogger.Interfaces;

namespace WebServiceLogger
{
    public class WebServiceLogAppender : AppenderSkeleton
    {
        private IClientService _clientService;

        //Url property is declared with tag in my log4net.config tag
        public string Url { get; set; }     
        public WebServiceLogAppender()
        {
            InitializeClientService(); 
        }

        protected override void Append(LoggingEvent loggingEvent)
        {
            var log = RenderLoggingEvent(loggingEvent);
            _clientService.SendLog(log);
        }

        private void InitializeClientService()
        {
            var smtpClientFactory = new SmtpClientFactory();
            var mailMessageFactory = new MailMessageFactory();
            _clientService = new EmailClientService(smtpClientFactory,mailMessageFactory)
        }
    }
}

And one class that implement IClientService

using System.Net.Mail;
using WebServiceLogger.Interfaces;

namespace WebServiceLogger
{
    public class EmailClientService : IClientService
    {
        private readonly SmtpClient _smtpClient;
        private readonly IMailMessageFactory _mailMessageFactory;

        public EmailClientService(ISmtpClientFactory smtpClientFactory, IMailMessageFactory mailMessageFactory)
        {
            _smtpClient = smtpClientFactory.Create("username", "password");
            _mailMessageFactory = mailMessageFactory;
        }

        public void SendLog(string logToSend)
        {
            var message = _mailMessageFactory.CreateMailMessage(logToSend);
            _smtpClient.Send(message);
        }
    }
}

I don't want cable the webservice inside this class but i have only a parameterless constructor.

The answer is , can i specify the object of one class that implement the IClientService interface in log4net.config ?

2

There are 2 answers

0
stuartd On

Because log4net creates the appenders itself, it isn't possible to use constructor or property injection, and you cannot specify a class in the config: you could if you really wanted to just specify the name of the class implementing IClientService in the config and create it at runtime, but that's not really advisable.

If your intention is to reduce code duplication, then creating a base class for your custom logger will do that: then you can mix and match the appenders in your config file.

public abstract class BaseWebServiceLogAppender : AppenderSkeleton
{
    protected IClientService _clientService;

    //Url property is declared with tag in my log4net.config tag
    public string Url { get; set; }

    public BaseWebServiceLogAppender()
    {
        InitializeClientService();
    }

    protected override void Append(LoggingEvent loggingEvent)
    {
        var log = RenderLoggingEvent(loggingEvent);
        _clientService.SendLog(log);
    }

    protected abstract void InitializeClientService();
}

public class EmailWebServiceLogAppender : BaseWebServiceLogAppender
{
    protected override void InitializeClientService()
    {
        var smtpClientFactory = new SmtpClientFactory();
        var mailMessageFactory = new MailMessageFactory();
        _clientService = new EmailClientService(smtpClientFactory,mailMessageFactory)
    }
}

public class SmsWebServiceLogAppender : BaseWebServiceLogAppender
{
    protected override void InitializeClientService()
    {
        _clientService = new SmSClientService(…);
    }
}
0
kat On

Not sure which container you are using. In my case, I am using Unity and was able to run the appender instance through the container after its creation:

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() method will populate the injection property in the appender class:

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

    ...
}