I recently had to use log4net with ninject’s dependency injection framework and it’s logging extension. It took me a while to work it out and there didn’t seem to be a step by step guide to implementing it, so I decided to post one here.
This guide is in the context of implementing a Logger property from within a single class, in this case it was a RoleProvider to be used on a website. For the sake of the tutorial we’ll just create a simple class called SimpleRoleProvider, with a single method – GetRoles(string username):
namespace coalvilledave.tutorials.ninject { using System; using Ninject; using Ninject.Extensions.Logging; using Ninject.Extensions.Logging.Log4net; using Ninject.Modules; public class SimpleRoleProvider { private ILogger _logger; public SimpleRoleProvider() { } public string[] GetRoles(string username) { string[] results = null; if (username == "coalvilledave") { results = new string[] { "Devs", "Admins", "Bloggers" }; } else { _logger.Error("User {0} not found", username); results = new string[] { }; } return results; } } }
As you can see, pretty straightforward. All we need to do now is populate the _logger field. Our implementation of ILogger is provided by the Ninject.Extensions.Logging.Log4net.Infrastructure.Log4netLogger class, which wraps around the log4net logger class that we all know and love. The Ninject.Extensions.Logging.Log4net.Log4netModule binds an ILoggerFactory with a Log4netLoggerFactory class, which we then use to get our logger. We’ll be using an IKernel implementation to instantiate the ILoggerFactory, so let’s create a little method to return an IKernel (put it just below the parameterless constructor):
protected virtual IKernel CreateKernel() { var settings = new NinjectSettings { LoadExtensions = false }; //*** PLEASE NOTE, IN LATER VERSIONS OF THE NINJECT LOGGING EXTENSIONS, THE LOG4NET MODULE TYPE NAME IS Log4NetModule (capital 'N') *** return new StandardKernel(settings, new INinjectModule[] { new Log4netModule() }); }
Now all we need to do is plumb it all in. We’ll do this from the constructor so our class will initialise as soon as it is instantiated – add the following inside SimpleRoleProvider’s parameterless constructor:
var kernel = this.CreateKernel(); var loggerFactory = kernel.Get<ILoggerFactory>(); _logger = loggerFactory.GetCurrentClassLogger();
Ok, so now we just need to create a test harness of some sort, add the log4net specific stuff to the .config file and fire it up. I’ll just use a console app for this:
namespace coalvilledave.tutorials.ninject { using System; public class SimpleRoleProviderTester { public static void Main(string[] args) { log4net.Config.XmlConfigurator.Configure(); var roleProvider = new SimpleRoleProvider(); Console.WriteLine(roleProvider.GetRoles("coalvilledave")); Console.WriteLine(roleProvider.GetRoles("johnsmith")); // Will log an error } } }
The first line is VERY important, if you don’t call
log4net.Config.XmlConfigurator.Configure()
You WON’T be able to find any of the loggers specified in your configuration file.
Finally, lets configure a Logger in the App.config or Web.config file:
<?xml version='1.0'?> <configuration> <configSections> <section name='log4net' type='log4net.Config.Log4NetConfigurationSectionHandler, log4net'/> </configSections> <log4net> <appender name='TestLog' type='log4net.Appender.RollingFileAppender'> <file value='C:/ninjecttestlog.log'/> <appendToFile value='true'/> <rollingStyle value='Date'/> <datePattern value='ddMMyyyy'/> <layout type='log4net.Layout.PatternLayout'> <conversionPattern value='%date [%thread] %-5level %logger [%property{NDC}] - %message%newline'/> </layout> </appender> <root> <level value='INFO'/> <appender-ref ref='TestLog'/> </root> <logger name='coalvilledave.tutorials.ninject.SimpleRoleProvider' additivity='false'> <level value='INFO'/> <appender-ref ref='TestLog'/> </logger> </log4net> </configuration>
Job done! – when you run the console app, the log should appear in C:\ninjecttestlog.log