Latest Replies
Tuesday
Mar032009

Logging with Lokad Shared Libraries for .NET

Let's get back to the topic of using Lokad Shared Libraries and Autofac .NET IoC Container to structure and configure your applications.

In the first article we've talked about Structuring .NET Applications with Autofac. There was a stub for sample Module class for strongly-typed configuration.

Let's go on to explore the contents of the Module.Configure method that performs the actual work. We'll focus on logging in this article.

public sealed class Module : IModule
{
  public string ConnectionOptions { get; set; }
  public bool UseMockDataLayer { get; set; }
  public Mode Mode { get; set; }

  public Module()
  {
    Mode = Mode.Release;
  }

  public void Configure(IContainer container)
  {
    // if module is reused in Windows Azure:
    // if (RoleManager.IsRoleManagerRunning)
    //   ApplyOverridesFromRuntime();    

    var builder = new ContainerBuilder();

    // configure logging
    var logger = GetLoggerForWindows(Mode);
    builder.Register(logger);
    builder.Register(logger.Get("DefaultLog"));

    // configure exception capturing
    builder.Register(exceptionCounter);
    var exceptionCounter = ExceptionCounters.Default;   

    RegisterPolicy(builder);
    RegisterDal(builder);
    RegisterRuleBehavior(builder);
  }
  // more
}

As you can see, the first step is about configuring logging and making it available for any component resolved through the Autofac IoC.

GetLoggerForWindows method (we'll look into the implementation later) returns an instance of generic interface INamedProvider{ILog}. This interface essentially allows us to get a named logger (name of a logger is a category that could be used to filter messages later on):

INamedProvider<ILog> logger = ...;
ILog namedLog = logger.Get("DAL Log");

Both ILog and INamedProvider{T} interfaces are defined in the Lokad.Shared.dll and, thus, are not bound to any logging framework. Any component, that is resolved via the IoC Container, could get an instance of either of these to do its logging chores.

Here's how the ILog interface is defined in C#:

public interface ILog
{
  void Log(LogLevel level, object message);
  void Log(LogLevel level, Exception ex, object message);
  bool IsEnabled(LogLevel level);
}

As you can see, it is extremely easy to write your own implementation of this interface. Here's, actually, how the implementation of DebugLog could look like in C#:

public void Log(LogLevel level, object message)
{
  Debug.WriteLine(message, level.ToString());
}

public void Log(LogLevel level, Exception ex, object message)
{
  Debug.WriteLine(message, level.ToString());
  Debug.WriteLine(ex, level.ToString());
}

public bool IsEnabled(LogLevel level)
{
  return true;
}

And any such implementation will (thanks to the extension methods defined in Lokad.Shared.dll) provide out-of-the box rich functionality like:

log.Debug("Message");
log.Debug(ex, "Message with exception");
log.DebugFormat("Message #{0}", id);
log.DebugFormat(ex, "Message #{0}", id);

if (log.IsDebugEnabled())
{
    // some extensive logging
}

These helper methods are defined for following logging levels:

  • Debug - message is intended for debugging;
  • Warn - informatory message;
  • Info - the message is about potential problem in the system;
  • Error - some error has occurred;
  • Fatal - message is associated with the critical problem.

Additionally, Lokad.Shared.dll provides simple implementations of these simple loggers (in System.Diagnostics namespace):

  • TraceLog - prints messages to the Trace.Listeners.
  • DebugLog - prints messages to the Debug.Listeners.
  • NullLog - does not do anything.

Obviously, a production system would need more diverse logging capabilities, than these three interfaces. That's why Logging.Stack.dll allows to seamlessly plug powerful Apache log4net logging library into these interfaces.

Note, that this plugging will happen only in the configuration sections. So your business code would stay completely detached from the logging implementation details.

Apache log4net, is quite powerful, but configuring it could be quite complex. That's why Lokad.Stack.dll also provides a simplified fluent API to hide the complexity of configuring common usage scenarios.

Given all these details, here's finally how our GetLoggerForWindows implementation might look like:

static INamedProvider<ILog> GetLoggerForWindows(Mode mode)
{
  // configuring different logging based on our mode
  switch (mode)
  {
    case Mode.Release:
      // write all informational and higher events to
      // windows event log
      LoggingStack.UseEventLog(EventLogName, EventLogSource)
        .Filter(LogLevel.Info, LogLevel.Max);

      // dump all warning and higher messages to rolling text log
      LoggingStack.UseRollingLog("logs/errorlog.txt", 100.Kb(), 10)
        .Filter(LogLevel.Warn, LogLevel.Fatal);
      break;
    case Mode.Diagnostics:
      // dump all messages to daily log
      LoggingStack.UseDailyLog("logs/log.txt");
      break;
    case Mode.Debug:
      // Visual studio would get these messages
      return TraceLog.Provider;
    default:
      throw new ArgumentOutOfRangeException("mode");
  }
  return LoggingStack.GetLogProvider();
}

In the sample above we configure our logging infrastructure to write to (based on the configuration):

  • Windows Event log
  • Rolling textual log
  • Daily log
  • Trace

If that's not enough for you, you can load raw log4net settings from your config:

LoggingStack.UseConfig();
return LoggingStack.GetLogProvider();

By the way, thanks to the power of ILMerge and for your convenience, Lokad.Stack.dll already contains a version of log4net.dll inside. So you don't have to bother about getting it (neither you have to bother about looking for its open-source license for redistribution purposes, since it is already included in common ReadMe.txt distributed with the libraries).

Latest Autofac is packaged in there also.

That's it for the second article about configuring logging in your .NET applications that use Lokad Shared Libraries.

In the third article in the series we'll talk about configuring lightweight exception and performance counters to capture essential execution information about our application.

Meanwhile you can:

  • stay tuned to RSS feed for the updates;
  • explore Lokad Shared Libraries;
  • Share you thoughts about the logging integration capabilities provided by the libraries. Are they simple and powerful enough for you to use them (or pieces of the code) in your projects?
« Some OSS News on Lokad and Autofac | Main | Building VSTO Solutions Without Visual Studio »

References (2)

References allow you to track sources for this article, as well as articles that were written in response to this article.