Extensibility - Logging Frameworks


The 'Direct Project' currently uses NLog as the logging framework, however many organizations will have already settled on other logging frameworks such as Log4Net or the Logging Application Block. In order to slide in your own logging framework, or change the behavior of NLog from the reference implementation you only need to supply your own assembly that includes implementations of two interfaces:
  • ILogger
  • ILogFactory

In the csharp\samples\Log4NetDiagnosticsSample you will find the code below that has been provided as an example implementation of an alternative logging framework.

ILogger Implementation


Of the two interfaces, the ILogger is the most straightforward to implement. Below is a sample of the Log4Net implementation, just showing the Debug level. The same pattern should be used for the Error, Fatal, Info, Trace and Warn levels.
private log4net.ILog Logger
{
    get { return m_logger; }
}
 
public bool IsDebugEnabled
{
    get { return Logger.IsDebugEnabled; }
}
 
public void Debug(string message)
{
    Logger.Debug(message);
}
 
public void Debug(object obj)
{
    Logger.Debug(obj);
}
 
public void Debug(string message, params object[] args)
{
    Logger.DebugFormat(message, args);
}
 
public void Debug(string message, Exception exception)
{
    Logger.Debug(message, exception);
}
 

ILogFactory Implementation


The ILogFactory interface provides a factory method to return the correct ILogger interface for the named logger or logger for a specific type. Both NLog and Log4Net allow a logger to be created that uses either a named string or the type of the object itself. Below is a sample of wiring up the log4net configuration that makes use of the LoggingSettings class.
public class Log4NetLogFactory : ILogFactory
{
    public Log4NetLogFactory(LogFileSettings settings)
    {
        var patternLayout = new log4net.Layout.PatternLayout(
            "%date [%thread] %level %logger - %message%newline");
        patternLayout.ActivateOptions();
 
        RollingFileAppender appender
            = new RollingFileAppender
                  {
                      Name = "rolling-appender",
                      File = Path.Combine(settings.DirectoryPath, settings.NamePrefix + ".log"),
                      AppendToFile = true,
                      MaxSizeRollBackups = 10,
                      RollingStyle = RollingFileAppender.RollingMode.Date,
                      Layout = patternLayout,
                      LockingModel = new FileAppender.MinimalLock()
                  };
        appender.ActivateOptions();
 
 
        log4net.Config.BasicConfigurator.Configure(appender);
 
        Hierarchy h = (Hierarchy)LogManager.GetRepository();
        Logger rootLogger = h.Root;
        rootLogger.Level = h.LevelMap[ConvertLogLevel(settings.Level).ToString().ToUpper()];
    }
 
    public ILogger GetLogger(string name)
    {
        return new Log4NetLogger(LogManager.GetLogger(name));
    }
 
    public ILogger GetLogger(Type loggerType)
    {
        return new Log4NetLogger(LogManager.GetLogger(loggerType));
    }
 
    private static Level ConvertLogLevel(LoggingLevel level)
    {
        switch (level)
        {
            case LoggingLevel.Debug:
                return Level.Debug;
            case LoggingLevel.Error:
                return Level.Error;
            case LoggingLevel.Fatal:
                return Level.Fatal;
            case LoggingLevel.Info:
                return Level.Info;
            case LoggingLevel.Off:
                return Level.Off;
            case LoggingLevel.Trace:
                return Level.Trace;
            case LoggingLevel.Warn:
                return Level.Warn;
        }
 
        throw new ArgumentException("Unknown logging level - " + level);
    }
}
 

Wiring it up

The last step is to reference your new logging adapter library from the smtpAgent project and change the following line that initializes the container:
// NLog adapter
IoC.Initialize(new SimpleDependencyResolver())
    .Register<ILogFactory>(new NLogFactory(settings.LogSettings))
 
// log4net adapter
IoC.Initialize(new SimpleDependencyResolver())
    .Register<ILogFactory>(new Log4NetLogFactory(settings.LogSettings))
 
And that's all there is to it. Of course, you will most likely want to vary how you are configuring your own logging framework to suit your needs. Do keep in mind that if you are using the smtpEventHandler registered with smtpserver there is no corresponding app.config to place configuration of your logging framework. If you want this specified in a configuration file you will need to write the configuration portion of your ILogFactory appropriately.