What is log4net

If you have never heard about log4net, this article isn’t immediately suited for you. Dimitri Clement wrote a few articles about log4net and how to use it:

You can also visit the log4net website.

Basic example

People who use log4net know that you have to explicitly configure your logger and retrieve an instance, before using it.

A basic C# example would be:

using log4net;
using log4net.Config;

public class MyLogger
{
    private static readonly ILog logger =
          LogManager.GetLogger(typeof(MyLogger));

    static MyLogger()
    {
        DOMConfigurator.Configure();
    }

    static void Main(string[] args)
    {
        logger.Debug("Here is a debug log.");
        logger.Info("... and an Info log.");
        logger.Warn("... and a warning.");
        logger.Error("... and an error.");
        logger.Fatal("... and a fatal error.");
    }
}

Your app.config or web.config would look like:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="log4net"
           type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
    </configSections>
    <log4net>
        <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
            <param name="File" value="MyLogFile.txt" />
            <param name="AppendToFile" value="true" />
            <layout type="log4net.Layout.PatternLayout">
                <param name="Header" value="[Header]\r\n" />
                <param name="Footer" value="[Footer]\r\n" />
                <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n" />
            </layout>
        </appender>
        </appender>

        <root>
            <level value="INFO" />
            <appender-ref ref="LogFileAppender" />
        </root>
    </log4net>
</configuration>

 

My logging framework

As you can see in the example above, you need to do some plumbing to “activate” your logger. Also, you need to add stuff in your application configuration file (app.config | web.config).

When I want to start logging in an existing application or in a new application, I don’t want to copy/paste this code all the time. That’s why I created a tiny framework. Basically, all I want to do is configure my log4net appender and start logging like:

Logger log = new Logger();
log.LogInfo("Hello world");

Also, I don’t like it when there is too much stuff in my application config. So I’d like to separate the log4net configuration from my app.config.

So let’s take a look at the implementation.

Implementation

First, I create a “Class Library” project. Add the log4net.dll as a reference. What I like to do when developing framework libraries, is to export the result assemblies to a separate “output” folder. To do this, go to the project’s properties, open the Build Events tab and add the following code in the post-build event textbox:

REM "$(SolutionDir)OutputLibrary\Logging"
IF NOT EXIST "$(SolutionDir)OutputLibrary\Logging" mkdir "$(SolutionDir)OutputLibrary\Logging"
if $(ConfigurationName) == Release copy "$(TargetDir)$(TargetFileName)" "$(SolutionDir)OutputLibrary\Logging\$(TargetFileName)"
if $(ConfigurationName) == Release copy "$(TargetDir)log4net.dll" "$(SolutionDir)OutputLibrary\Logging\log4net.dll"
if $(ConfigurationName) == Release copy "$(TargetDir)log4net.config" "$(SolutionDir)OutputLibrary\Logging\log4net.config"

Notice that I also copy a “log4net.config” file.

log4net provides the ability to use a configuration file other than the app.config or web.config. I’ve called the file log4net.config.

<configuration>
	<log4net debug="false">
        <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
            <param name="File" value="MyLogFile.txt" />
            <param name="AppendToFile" value="true" />
            <layout type="log4net.Layout.PatternLayout">
                <param name="Header" value="[Header]\r\n" />
                <param name="Footer" value="[Footer]\r\n" />
                <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n" />
            </layout>
        </appender>
        </appender>
        <root>
            <level value="INFO" />
            <appender-ref ref="LogFileAppender" />
        </root>
	</log4net>
</configuration>

In order to explicitly configure the log4net logger, I will place the configuration part in the AssemblyInfo of the project.

[assembly: AssemblyTitle("My Logging Framework")]
[assembly: AssemblyDescription("Facilitates logging with log4net, with custom layout patterns.")]

[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]

Next, I defined a contract for the Logger class

public interface ILogger
{
    void EnterMethod(string methodName);
    void LeaveMethod(string methodName);
    void LogException(Exception exception);
    void LogError(string message);
    void LogWarningMessage(string message);
    void LogInfoMessage(string message);
}

And of course the implementation:

public class Logger : ILogger
{
    #region Datamembers

    private static ILog log = null;

    #endregion

    #region Class Initializer

    public Logger()
    {
        log = LogManager.GetLogger(typeof(Logger));
        log4net.GlobalContext.Properties["host"] = Environment.MachineName;
    }

    #endregion

    #region ILogger Members

    public void EnterMethod(string methodName)
    {
        if (log.IsInfoEnabled)
        log.Info(string.Format(CultureInfo.InvariantCulture, "Entering Method {0}", methodName));
    }

    public void LeaveMethod(string methodName)
    {
        if (log.IsInfoEnabled)
            log.Info(string.Format(CultureInfo.InvariantCulture, "Leaving Method {0}", methodName));
    }

    public void LogException(Exception exception)
    {
        if (log.IsErrorEnabled)
                log.Error(string.Format(CultureInfo.InvariantCulture, "{0}", exception.Message), exception);
    }

    public void LogError(string message)
    {
        if (log.IsErrorEnabled)
                log.Error(string.Format(CultureInfo.InvariantCulture, "{0}", message));
    }

    public void LogWarningMessage(string message)
    {
        if (log.IsWarnEnabled)
                log.Warn(string.Format(CultureInfo.InvariantCulture, "{0}", message));
    }

    public void LogInfoMessage(string message)
    {
        if (log.IsInfoEnabled)
                log.Info(string.Format(CultureInfo.InvariantCulture, "{0}", message));
    }

    #endregion
}

You can add custom properties to a log4net object, like: “log4net.GlobalContext.Properties["host"] = Environment.MachineName;”. This means that I add a property called “host” and give it the machine name as default value. You can put anything you like here, just remember to reference it in your appender.

Using the logging framework

When you want to use the logging framework in your application, you need to add a reference to the class library we’ve just created, this way the MyLogger class is accessible.

A second step you need to take is to make sure the log4net.dll and log4net.config files make it to your output directory (usually bin/debug or bin/release). You could add the files directly to your project, create a post-build event or use another mechanism.

You’re ready to go. Happy logging!

Sources and Example

Download the logging framework sources with example.