Expose logging context

Sep 8, 2010 at 9:05 PM
Edited Sep 10, 2010 at 6:43 PM

Should slf "generically" expose the logging context (NDC, MDC, etc) of log4net and NLog? 

For an example of a logging abstraction that does this, see the log4net and NLog abstractions at the Castle project.  log4net and NLog loggers can be resolved as basic loggers (I think with an ILogger interface) or as "extended" loggers (IExtendedLogger).  From what I can tell, the extended logger interface exposes all of the "context" information (stacks and dictionaries) that can be exposed on log4net and NLog.  There is also a TraceSource based logger, but it is only exposed as ILogger.  I'm not sure what would happen if you tried to access the extended information on a TraceSource based logger, maybe an exception, maybe it just doesn't support an interface.

Anyway, without exposing the logging context generically, one would have to resort to referencing the specific logging platform's LogManager to set the context, thus defeating the purpose of using the abstraction.

Of course, there is the inherent conflict between limiting the facade to capabilities available in all logging platforms vs implementing/exposing capabilities that are available in some, but not all, logging platforms.  It seems reasonable to me to expose the context capability for all loggers (i.e. make it available from the ILogger interface or from the LogManager).  If a particular logging implementation (e.g. a TraceSource-based logger) does not support context information (or only supports some context information like System.Diagnostics.Trace.CorrelationManager.LogicalOperationsStack)), then the logger should return a do-nothing implementation.  This way, code can be written against the logging abstraction without having to worry about the "contex" properties possibly being null.  So, you could always code something like this:

ILogger logger = LogManager.GetLogger("HelloWorld");

logger.Info("Hello");

using (logger.Push("nesting level 1"))
{
  logger.Info("Inside <nesting level 1> context");
  
  using (logger.Push("nesting level 2"))
  {
    logger.Info("Inside <nesting level 2");
  }

  logger.Info("Inside <nesting level 1> context again");
}

Rather than having to do something like this:

ILogger logger = LogManager.GetLogger("HelloWorld");

logger.Info("Hello");

if (logger.Context != null)
{
  logger.Push("nesting level 1");
}

logger.Info("Inside <nesting level 1> only if logger.Context != null");

if (logger.Context != null)
{
  logger.Push("nesting level 2");
}

logger.Info("Inside <nesting level 2> only if logger.Context != null");

logger.Pop();

logger.Info("Inside <nesting level 1> again only if logger.Context != null");

logger.Pop();