X

Hiding loggers implementations using Unity

Loggers are one of most popular examples about interfaces for sure. And there are a lot of implementations of loggers. Some implementations are simple and yet powerful, some implementations may be more complex. All we have to is to select implementation we need and integrate it to our application. It seems like good idea at first place but as soon as we need to switch from one implementation to another we discover nasty dependencies we have to change in all places where we are logging. Let’s see how to avoid these dependencies.

Analysis

To keep our application independent from logger implementation we need an interface that all our loggers will use. Our own loggers can implement our interface directly and for third-party loggers we can write wrapper classes that implement our interface. This way or other we logger interface.

Besides errors we may want to write also other messages to error log. We may need informative messages, warnings and debugging messages. To support different message types we need an enumerator.

Design

Our analysis gave us two objects: interface for loggers and enumerator for log message types. Our logger class looks like this.

In code we have ILogger interface and LogEntryType enumerator.

public interface ILogger
{
    void Log(LogEntryType type, string message);
    void Log(Exception exception);
}

public enum LogEntryType
{
    Info,
    Warning,
    Error
}

LogEntryType is very simple here because it is for example purposes. In real life this enum may be way longer than here. Good example is TraceSeverity enum from my blog post SharePoint: Writing messages to ULS (Unified Logging System).

Implementing ILogger

Let’s create now two logger implementations. One of these classes is our own logger class and the other one will be wrapper for log4net logger.

DebugLogger

DebugLogger is simple logger that writes all log messages to console. It does nothing special – it writes all the output to debug window.

public class DebugLogger : ILogger
{
    public void Log(LogEntryType entryType, string message)
    {
        var logMessage = GetLogMessage(entryType, message);
        Debug.WriteLine(logMessage);
    }

    public void Log(Exception ex)
    {
        var logMessage = GetLogMessage(LogEntryType.Error, ex.ToString());
        Debug.WriteLine(logMessage);
    }

    private string GetLogMessage(LogEntryType entryType, string message)
    {
        var messageBuilder = new StringBuilder();
        messageBuilder.Append(DateTime.Now.ToLongTimeString());
        messageBuilder.Append(" ");
        messageBuilder.Append(Enum.GetName(typeof(LogEntryType), entryType));
        messageBuilder.Append(" ");
        messageBuilder.Append(message);
        return messageBuilder.ToString();
    }
}

Output of DebugLogger is shown on following screenshot.

Log4NetLogger

Log4NetLogger is simple wrapper class. It only writes messages to log4net log. All other details are handled by log4net.

public class Log4NetWrapper : ILogger
{
    private static readonly ILog log = LogManager.GetLogger(typeof(Log4NetWrapper));

    static Log4NetWrapper()
    {
        XmlConfigurator.Configure();
    }

    public void Log(LogEntryType entryType, string message)
    {
        lock (log)
        {
            switch (entryType)
            {
                case LogEntryType.Error:
                    log.Error(message);
                    break;
                case LogEntryType.Warning:
                    log.Warn(message);
                    break;
                default:
                    log.Info(message);
                    break;
            }
        }
    }

    public void Log(Exception ex)
    {
        lock (log)
        {
            log.Error(ex);
        }
    }
}

In the case of file output we get result like this.

Breaking dependencies

Now it’s time to add logging support to our application. All logging clients must see only ILogger interface and LogEntryType enum. Plus one thing more – they have to know how to get logger. We will use Unity as IoC container. We want to keep information about mappings in application configuration file. To make unity read this configuration we need some place where we can initialize Unity. Look at Main() method (and Unity configuration) from my blog entry Unity and singletons (it’s not long). Now, if we need some logging we can ask logger simply by writing the following code.

var logger = container.Resolve<ILogger>();

To keep Unity logic away from your classes you can use some wrapper class that uses Unity IoC container internally. Also it is possible to write generic wrapper for IoC containers so your code knows only one concrete IoC wrapper class. In this case IoC implementation details are hidden for your client code.

Liked this post? Empower your friends by sharing it!

View Comments (9)

  • By doing this, your log4net logger will only be linked to the Log4NetWrapper class and log4net filters won't work.

  • Log4NetWrapper is the only point in *MY* code where something really knows about log4net. All logic must be carried out by this class.

    As you can see I have pretty simple and thin interface for logging and I can easily change implementation of logger or logging logic.

  • DUDE - WHY DID U PUT LOCK in your code?
    this is a huge mistake and has massive impact on performance!
    Log4net is thread safe by design and you don't have to use any thread safe code when accessing it!
    Any calls to the logger!

    Please - do not use it.

  • Sorry, my bad - I was bad code copy-paste gangsta again. I try to be better in (near) future :)

  • micro, you don't lose ability to conf the logger because configuration is in web.config or app.config file. There is no way to make specifics of concrete logger visible in classes where these specifics are not welcome.

Related Post