Adding custom log levels to log4net

Logging is a critical piece of infrastructure for any application. On most of my projects, I use log4net because it’s a tried and tested framework that does exactly what it says on the box and stays out of your way. Today, I wanted to start logging security events in an application I’m building. By security events, I mean things like users logging in and out, changing passwords, and so on.

I’m currently using text files to hold all my logs because I think logging infrastructure should be as simple as possible. For all the benefits of using a database to hold logs, I think it adds too many points where things could go wrong, potentially resulting in lost or missing log data. Now, when you’re keeping your logs in plain-text, you need to be careful to structure the log output so you can parse the data easily. The most straightforward way to do this without having to pepper your log data itself with magic strings is to use appropriate log levels.

Out of the box, log4net provides five different log levels:

  1. Debug
  2. Error
  3. Fatal
  4. Info
  5. Warn

I wanted to add things like “Login” and “Logout” to this list. It turns out that this is relatively straightforward to accomplish with log4net.

The first thing you need to do is to create and register your new levels with the LogManager like this:

log4net.Core.Level authLevel = new log4net.Core.Level(50000, "Auth");
log4net.LogManager.GetRepository().LevelMap.Add(authLevel);

It’s important that you do this before configuring log4net.

Adding some extension methods makes it dead simple to start using the new log levels:

public static class SecurityExtensions
{
    static readonly log4net.Core.Level authLevel = new log4net.Core.Level(50000, "Auth");

    public static void Auth(this ILog log, string message)
    {
        log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, 
            authLevel, message, null);
    }

    public static void AuthFormat(this ILog log, string message, params object[] args)
    {
        string formattedMessage = string.Format(message, args);
        log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType,
            authLevel, formattedMessage, null);
    }

}

And that’s it – now I can start using my new “Auth” logging level on any instance of ILog like this:

SecurityLogger.AuthFormat("User logged in with id {0} from IP address {1}", id, Request.UserHostAddress);

No Comments