Monday, March 14, 2011

Injecting a logger in Caliburn.Micro



CM defines a ILog interface. The LogManager class implements a NullLog. It's CM default implementation. As it is, it's not very useful. I looked around and found How To Do Logging with Caliburn.Micro. It works all right on my WPF project. The question now is I don't want to specify my logger in code, I want to use DI/IoC pattern (Dependency Injection/Inversion of Control). This means I want to specify the logger in my App.config. Sounds easy, been there, done that.

1) Create a project Logger.
2) Add a reference to the Caliburn.Micro assembly you are using (be it WPF, Silverlight or WP7).
3) Add references to NLog and log4net and whatever logging library you might want to use. I'm using NLog 1.0 Refresh and it works all right.
4) Copy the ILog implementations from How To Do Logging with Caliburn.Micro to this project.
5) Change the namespace of the classes to Logger and make the classes scope public.
6) Build the Logger project just to make sure everything is ok.
7) Go back to your main project and remove all references to NLog, log4net, etc.
8) On your main project open App.config and a add a line in the appSettings section:
<add key="Logger" value="Logger.NLogLogger, Logger" />

Now we need a method to inject the class as our logger.

9) On your main application, add a class like this:

using System;
using System.Configuration;
using Caliburn.Micro;

namespace CaliburnMicroWpfApp.Framework
{
    /// <summary>

    /// Manages the injection of the logger.
    /// </summary>
    public static class LoggerFactory
    {

        /// <summary>
        /// Creates logger instance.
        /// </summary>
        /// <returns>The ILog instance.</returns>
        public static ILog GetLogger(Type type)
        {
            Type loggerType;

            var loggerTypeName = ConfigurationManager.AppSettings["Logger"];
            if (!string.IsNullOrEmpty(loggerTypeName))
                loggerType = Type.GetType(loggerTypeName);
            else
                throw new NullReferenceException("Logger");

            if (loggerType == null)
                throw new ArgumentException(string.Format("Type {0} could not be found", loggerTypeName));

            return (ILog)Activator.CreateInstance(loggerType, type);
        }
    }
}


10) Call the GetLogger method in your bootstrapper class. Mine is like this:

using Caliburn.Micro;
using CaliburnMicroWpfApp.Framework;
using CaliburnMicroWpfApp.ViewModels;

namespace CaliburnMicroWpfApp
{
    public class DefaultBootstrapper : Bootstrapper
    {
        static DefaultBootstrapper()
        {
            LogManager.GetLog = type => LoggerFactory.GetLogger(type);
        }
    }
}


11) Don't forget to add a nlog section to your App.config or a NLog.config file.

That's it.