Saturday, April 16, 2011

Gaining access to underlying log levels that are not exposed in log4net

Within log4net, the ILog interface exposes five general log levels that are typically used within an application and they are:
  1. Debug
  2. Error
  3. Fatal
  4. Info
  5. Warn
Now, what if you want access to alternative underlying log levels that the system supports yet the ILog interface does not expose?  What needs to be done in order to achieve this goal?

There are a few ways of achieving this goal and what follows will be what I believe to be the most simplistic method possible using the technology available to us at this point in time.  The alternative methods, I will leave up to you, the reader, to research for yourself as they are not the target of this article, nor is it the intention to compare the alternatives with what has been chosen to implement.

We are going to use extension methods to achieve this goal.

    /// <summary>
    /// Extension methods for <see cref="ILog"/> implementations that provide the callers access to the <see cref="Level.Finest"/> logging level.
    /// </summary>
    public static class LogExtensions
    {
        private static readonly Type DeclaringType = typeof(LogExtensions);

        /// <summary>
        /// Log a message object with the <see cref="Level.Finest"/> level
        /// </summary>
        /// <param name="log">The ILog interface is use by application to log messages into the log4net framework.</param>
        /// <param name="message">The message object to log.</param>
        public static void Finest( this ILog log, object message )
        {
            // NullLog returns null for 'Logger' property.
            if ( log.Logger == null )
                return;

            log.Logger.Log( DeclaringType, Level.Finest, message, null );
        }

        /// <summary>
        /// Log a message object with the <see cref="Level.Finest"/> level
        /// </summary>
        /// <param name="log">The ILog interface is use by application to log messages into the log4net framework.</param>
        /// <param name="message">The message object to log.</param>
        /// <param name="exception">The exception to log, including its stack trace.</param>
        public static void Finest( this ILog log, object message, Exception exception )
        {
            // NullLog returns null for 'Logger' property.
            if ( log.Logger == null )
                return;

            log.Logger.Log( DeclaringType, Level.Finest, message, exception );
        }

        /// <summary>
        /// Logs a formatted message string with the <see cref="Level.Finest"/> level.
        /// </summary>
        /// <param name="log">The ILog interface is use by application to log messages into the log4net framework.</param>
        /// <param name="format">A String containing zero or more format items</param>
        /// <param name="args">An Object array containing zero or more objects to format</param>
        public static void FinestFormat( this ILog log, string format, params object [] args )
        {
            // NullLog returns null for 'Logger' property.
            if ( log.Logger == null )
                return;

            if ( log.IsFinestEnabled() )
                log.Logger.Log( DeclaringType, Level.Finest,
                    new SystemStringFormat( CultureInfo.InvariantCulture, format, args ), null );
        }

        /// <summary>
        /// Checks if this logger is enabled for the <c>Finest</c> level.
        /// </summary>
        /// <param name="log">The logger instance to check.</param>
        /// <returns>
        ///     <c>true</c> if this logger is enabled for <c>Finest</c> events, <c>false</c> otherwise.
        /// </returns>
        public static bool IsFinestEnabled( this ILog log )
        {
            // NullLog returns null for 'Logger' property.
            if ( log.Logger == null )
                return false;
            return log.Logger.IsEnabledFor( Level.Finest );
        }
    }

As can be seen, the extension method class is fairly straight forward and easy to understand.

What may not apply for you, the reader, is the null check within each of the extension methods (see comments about NullLog).

The null checks are there because an NullLog ILog instance that is used within unit tests where the FINEST level of logging is not utilized within any assertions. If you have no use for such checks, feel free to remove them.

I hope that this snipped of code makes someones life easier.  Happy hacking!