Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An enhanced wrapper around the LOG4NET logging framework

0.00/5 (No votes)
26 Mar 2005 1  
An article about an enhanced wrapper around the popular logging framework LOG4NET.

An example logging output inside a console window

Introduction

(For the latest changes, please see the History section below)

In this article, I'll show you a class that simplified my life in the last years when it comes to logging (error) messages from a .NET application, no matter whether it is a Windows GUI application, a Windows Service or a Windows web application.

By using the single class described in this article you should be able to provide meaningful logging information in every state of your application with a minimum of effort and a maximum of flexibility.

Background

Again (like in some of my other articles), I did not write the logging part itself from scratch but rather added my own code to the popular and well-tested superb logging framework LOG4NET.

The main class for logging is the class LogCentral. I tried to hide all the LOG4NET classes and interfaces inside my class so that the user of the LogCentral class never gets in direct contact with the LOG4NET services. The reason was to allow maximum flexibility in that if I ever want to change the underlying logging engine to another engine, no code changes other than inside the LogCentral class itself are necessary.

Please note: A possible negative effect of the hiding of the LOG4NET is the fact that not all of the extended LOG4NET features are available to the user of the LogCentral class. In practice this was never a problem inside my small- to mid-size projects, but could be if you try to do logging to the extreme. In such cases it would probably be better to directly use a logging framework like LOG4NET and do not use the LogCentral class.

Supported Features

The LogCentral class provides the following features:

Single entry point for logging

To log a message, you simply use the static property LogCentral.Current to access an always-available instance of the logging class.

Logging of different types of messages by simple function calls

The LogCentral class provides easy access to actually log a message by providing overloads for several logging types.

Example: The following call logs an informational message.

...
 
LogCentral.Current.LogInfo( 
    "This is an informational message." );
 
...

The following functions are defined inside the LogCentral class (parameters are omitted for better readability):

  • public void LogInfo() - Logs an informational message.
  • public void LogError() - Logs an error message, e.g. inside a catch block.
  • public void LogDebug() - Logs a debug message that should not appear in release builds (it is up to you to properly configure the class through the configuration file so that the debug message really is not logged during release builds, though).
  • public void LogWarn() - Logs a warning message.
  • public void LogFatal() - Logs a fatal message.

Standard configuration through the known LOG4NET XML configuration schema

Although I said that the LogCentral class hides all the LOG4NET code from the user of the class, I did not provide my own configuration file syntax. Therefore, the configuration for the LogCentral class is exactly the same as for the LOG4NET itself. All documentation for configuring the LOG4NET framework applies to the LogCentral class as well.

An example configuration inside your standard application configuration file (namely "YourExeName.exe.config" for Windows executables or "Web.config" for Web Services and web applications) might look like following:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section 
      name="log4net" 
      type="System.Configuration.IgnoreSectionHandler" />
  </configSections>
  
  <log4net>
    <appender 
      name="RollingLogFileAppender" 
      type="log4net.Appender.RollingFileAppender">
      <param name="File" value="Test.log" />
      <param name="AppendToFile" value="true" />
      <param name="MaxSizeRollBackups" value="9" />
      <param name="MaximumFileSize" value="10MB" />
      <param name="RollingStyle" value="Size" />
      <param name="StaticLogFileName" value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <param 
          name="Header" 
          value="\r\n\r\n---------------------------------------------\r\n" />
        <param 
          name="Footer" 
          value="\r\n---------------------------------------------\r\n\r\n" />
        <param name="ConversionPattern" value="%d [%t] %-5p - %m%n" />
      </layout>
    </appender>
  
    <appender 
      name="ColoredConsoleAppender" 
      type="log4net.Appender.ColoredConsoleAppender">
      <mapping>
        <level value="ERROR" />
        <foreColor value="White" />
        <backColor value="Red" />
      </mapping>
      <mapping>
        <level value="DEBUG" />
        <backColor value="Green" />
      </mapping>
      <mapping>
        <level value="INFO" />
        <foreColor value="White" />
      </mapping>
  
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="%-5p: %m%n" />
      </layout>
    </appender>
  
    <root>
      <level value="ALL" />
      <appender-ref ref="RollingLogFileAppender" />
      <appender-ref ref="ColoredConsoleAppender" />
    </root>
  </log4net>
</configuration>

Automatically logging to additional destinations

In addition to the logging destinations ("appenders") defined in your configuration file, the LogCentral class tries to log each message to the following destinations as well:

Event handler for adding additional text before a message gets logged

A common requirement in my applications was to provide additional information to the messages that actually get logged, depending on the current context of the application. E.g. when an SQL query failed, I wanted to log the actual SQL query too, not just the exception that was raised during the execution of the SQL query.

For such scenarios, I added support to provide event handlers by the application to the LogCentral class that get called just before a message is actually being logged. The handler can add additional information to the message being logged.

Example

The following code adds a handler for the event. Usually you do that at the start of the application:

...
  
// Add handler.
LogCentral.Current.RequestMoreInformation += 
    new LogCentralRequestMoreInformationEventHandler(
    Current_RequestMoreInformation );  
...

The actual handler Current_RequestMoreInformation could be defined as following. In this example, the variable this.counter is some class member that gets modified by certain methods and is logged whenever a severe message is being logged.

/// <summary>
/// This handler is called before a message is actually
/// being logged. Use this handler to provide more detailed 
/// information to the message being logged.
/// </summary>
private static void Current_RequestMoreInformation(
    object sender, 
    LogCentralRequestMoreInformationEventArgs e )
{
    // Only add for certain types.
    if ( e.Type==LogCentralRequestMoreInformationEventArgs.LogType.Error ||
    e.Type==LogCentralRequestMoreInformationEventArgs.LogType.Fatal ||
    e.Type==LogCentralRequestMoreInformationEventArgs.LogType.Warn )
    {
        // Add information that would otherwise not be visible
        // to the logging class.
        e.MoreInformationMessage += 
            string.Format(
            "The current counter value is '{0}'.",
            this.counter );
    }
}

Now whenever a message is logged, the Current_RequestMoreInformation handler is being called by the logging class.

Default handlers provided

The LogCentral class itself uses this event internally to provide context information for the following items:

  • HTTP information

    When running inside a web context, several information like requested URI, referer URI, user host name, session information etc. is being logged.

  • Assembly information

    Information about the assemblies of the running application are being logged. These information include assembly path, assembly date, assembly version, etc.

  • Environmental information

    Information about the local Windows environment is being logged. These information include current logged in user, user domain, machine name, CLR version, current working directory, etc.

  • Network information

    Information about the host name and the IP addresses of the computer running the application are being logged.

Please note: The default handlers only add the context information if the logged message is of type "warn", "error" or "fatal".

Event handler after a message got logged

For completeness, there also exists an event that gets raised after a log occurred. You can add your own handler to that event to perform miscellaneous tasks after a message got logged, e.g. cleaning up resources or something similar.

Example

The following code adds a handler for the event. Usually you do that at the start of the application:

...
  
// Add handler.
LogCentral.Current.Log += 
    new LogCentralLogEventHandler(
    Current_Log );
  
...

The actual handler Current_Log could be defined as following. In this example it simply writes a notify message to the Debug output.

/// <summary>
/// This handler is called after each call to one of the 
/// LogError, LogDebug, etc. function.
/// </summary>
private static void Current_Log(
    object sender, 
    LogCentralLogEventArgs e )
{
    System.Diagnostics.Debug.Write( "Log occured." );
}

Other features in brief

  • Protecting of passwords

    Every message that gets logged is being checked for potentially security related keyword like "password" or similar. If such a keyword is detected, a warning message ("...illegal words contained...") is actually being logged instead of the message itself.

  • Provide access to the path of the configuration file

    By using the ConfigurationFilePath property, you can read the full path of the current configuration property, e.g. "C:\Inetpub\WwwRoot\MyWebApplication\Web.config". Sometimes this can be useful when you, for example, want to read other files from exactly the same folder where the configuration file is stored.

Using the Code in your Projects

The code download for this article contains a small example project as well as precompiled versions (.NET version 1.1, debug and release) of the library. To use it in your own projects you have (at least) two possibilities:

  1. Simply copy the content of the "Release" folder (namely the files "log4net.dll" and "ZetaLogger.dll") to a folder of your choice and add a reference to it by using the Add reference command in Visual Studio .NET 2003.
  2. Copy and add the source file "LogCentral.cs" to your project so that it gets compiled with your project. Please note that you still need to add a reference to the "log4net.dll" library.

I recommend that you first take a look at the example application and then use the library inside your own projects.

Conclusion

In this article, I've shown you a class for adding logging capabilities to your application. I'm using the class since more than two years, slightly enhancing the functionality from time to time. Hopefully you'll find this class useful, too.

For questions, comments and remarks, please use the commenting section at the bottom of this article.

References

  1. The LOG4NET framework homepage.

History

  • 2005-03-26

    Created first version of article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here