tags:

views:

87

answers:

3

Ok, here's what I really want to accomplish: I'd like to separate my 'normal' logging/auditing from my security logging/auditing. Kinda similar to how the Windows Event Logs split Application Events from Security Events.

If I could create a custom LogLevel, e.g. LogLevel.AuditSuccess or LogLevel.AuditFailure, then I could set my config file rules to equal these and output those events. For example,

<logger name="*" levels="AuditSuccess,AuditFailure" writeTo="target1"/>
<logger name="*" levels="DEBUG,INFO" writeTo="target1"/>

Then I could just use 1 table, record the "Level" in a column, and be able to search and sort my data using this column info. (I don't think we can create a custom LogLevel.)

However, the only resolution I could come up with is to use 2 loggers per class--with each logger being saved to a different target. However, this seems like overkill, especially if I need to add another similar target type.

<logger name="myNamespace.*" levels="INFO,ERROR" writeTo="target1"/>
<logger name="mySecurityLogger" levels="INFO,ERROR" writeTo="target2"/>

public class MyClass {
    private static Logger _logger = LogManager.GetCurrentClassLogger();
    private statac Logger _loggerSecurity = LogManager.GetLogger("mySecurityLogger");
    ...
}

With this, I could create two database targets--each with a different table--and then create 1 rule for each destination target.

Any suggestions?

A: 

The logging framework we use has a "category" property that can be set when logging. Works perfectly. I suppose NLog doesn't have that, huh?

Pedro
+2  A: 

This might not be what you are after, but bear with me ...

You could wrap NLog such that you can log with your own "loggers". Have a look at Common.Logging for .NET or SLF for examples of how to wrap NLog (they are full logging abstractions, so they are more complex than what you are after, but you might pick up some good ideas). Also see here (probably should look here first if you think that you might be interested in simply wrapping or subclassing the NLog Logger) for examples of how to wrap (or subclass) NLog correctly (note that the key is to pass the type of your wrapped/subclassed logger to NLog's Log method).

So, you might have something like this (abbreviated):

//Wrapped logger that you could create once in a class and use to log both 
//"normal" messages and "audit" messages.  NLog log level is determined by the 
//logger configuration for the class.
public class MyLogger
{
  private Logger logger; //NLog logger

  public MyLogger(string name)
  {
    logger = LogManager.GetLogger(name);
  }

  public void Info(string message)
  {
    if (!logger.IsInfoEnabled) return;

    Write(LogLevel.Info, LogLevel.Info.ToString(), message);
  }

  public void AuditSuccess(string message)
  { 
    if (!logger.IsInfoEnabled) return;

    Write(LogLevel.Info, "AuditSuccess", message);
  }

  private void Write(LogLevel level, string customLevel, string message)
  {
    LogEventInfo le = new LogEventInfo(level, logger.Name, message);
    le.Context["CustomLogLevel"] = customLevel;
    logger.Log(typeof(MyLogger), le);
  }
}

This will give you level-specific logging methods corresponding to your custom levels. It will also give you the ability to output a custom column containing your "custom level" (using the event-context layout renderer). It does not give you the ability to turn "AuditSuccess" level logging on or off. It must still be controlled by the built-in levels.

Alternatively, you could include two loggers in your wrapped class and use one for built-in levels and the other for custom levels. Your wrapper might look like this:

//Wrapped logger that you could create once in a class and use to log both "normal"
//and "audit" messages.  NLog log level for each type of message is controllable
//separately since the logger wrapper actually wraps two logger.
public class MyLogger
{
  private Logger logger; //NLog logger
  private Logger auditLogger;

  public MyLogger(string name)
  {
    logger = LogManager.GetLogger(name);
    auditLogger = LogManager.GetLogger("AuditLogger");
  }

  public void Info(string message)
  {
    if (!logger.IsInfoEnabled) return;

    Write(logger, LogLevel.Info, LogLevel.Info.ToString(), messsage);
  }

  public void AuditSuccess(string message)
  { 
    if (!auditLogger.IsInfoEnabled) return;

    Write(auditLogger, LogLevel.Info, "AuditSuccess", message);
  }

  private void Write(Logger log, LogLevel level, string customLevel, string message)
  {
    LogEventInfo le = new LogEventInfo(level, log.Name, message);
    le.Context["CustomLogLevel"] = customLevel;
    log.Log(typeof(MyLogger), le);
  }
}

You still would have the same restrictions as listed above, but you could at least control "audit/security" logging separately from "normal" logging.

In either case, you can send the output from all loggers to the same database and just do your queries on your "custom level" column rather than the "level" column. Depending on how many security/auditing "categories" you have, you might even want to make a concrete hierarchy such that you can take advantage of the logger hierarchies in the NLog config file:

Security Security.Audit Security.Audit.Success Security.Audit.Failure Security.Login Security.Login.Success Security.Login.Failure Health Health.Heartbeat Health.Whatever

Then you could "turn on" Security or Security.Audit or ..Failure (not sure if that last one works or not).

Hope this helps.

wageoghe
A: 

Couldn't you use two differently named loggers in your class, output to the same target, and use the ${logger} layout renderer as a field value?

Honestly, if these events are so important in the application domain, perhaps recording them through the logging framework is the wrong way to do it. Maybe it should be a more explicitly modeled concept in your application. Of course, if you are required to store the event data in the same place as the other log messages, you might not have that choice.

Otherwise, I tend to veer away from custom logging levels, as they have never really proven worth the hassle of adding them.

qstarin