8
votes

When I wanted to use the different logging levels available with log4net, I found out that I needed to use the method ILogger.Log, which looks like this:

void Log(Type callerStackBoundaryDeclaringType, Level level, object message, Exception exception);

We can get an ILogger reference from our usual ILog reference to call this method and it is usually suggested to wrap this call in an extension method. What we get is the following:

//typical log4net ILog reference
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

//suggested extension method
public static void Trace(this ILog logger, object message, Exception e)
{
    logger.Logger.Log(MethodBase.GetCurrentMethod().DeclaringType, log4net.Core.Level.Trace, message, e);
}

I have noticed how the reflection call to get the declaring type looks redundant. We have already passed the declaring type to GetLogger when we retrieved our ILog reference. Why did we need to pass another type reference to the method Log, which we are invoking on an ILogger reference which we have retrieved from our ILog reference. More importantly, the declaring type in the extension method would be the class that contains the extension method. Logging all your Trace logs with a class name like Log4NetExtensions would make no sense. Finally, reflection is supposed to be expensive, even though reflecting on the current method might be cheaper, calling reflection code every time we log does not sound right. So I decided to make a test:

//I just created a logger
var logger = logManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

//and then I called Log with different type parameters
logger.Logger.Log(MethodBase.GetCurrentMethod().DeclaringType, log4net.Core.Level.Info, "hello!", null);
logger.Logger.Log(logger.GetType(), log4net.Core.Level.Info, "hello!", null);
logger.Logger.Log(null, log4net.Core.Level.Info, "hello!", null);
logger.Logger.Log(typeof(System.Console), log4net.Core.Level.Info, "hello!", null);

And the resulting logs looked like the following:

INFO  2015-05-29 11:06:57,200 [9] Foo.Program - hello!
INFO  2015-05-29 11:06:57,207 [9] Foo.Program - hello!
INFO  2015-05-29 11:06:57,207 [9] Foo.Program - hello!
INFO  2015-05-29 11:06:57,207 [9] Foo.Program - hello!

So, it seems that the type parameter to this method is ignored. This is very puzzling. Can anyone confirm or deny these results? Does anyone know why this works the way it does? I intend to create my extension method with null for this parameter. Would you suggest the same? Am I missing something?

The following are questions whose answers suggest using the declaring type in the extension method:

Log4net creating custom levels

Why isn't there a trace level in log4Net?

Log4net, how to log a verbose message?

2

2 Answers

5
votes

The type you pass to LogManager.GetLogger is used to name the returned logger, which is why all your logging calls are labelled 'Foo.Program'.

The type passed to Logger.Log though is "The declaring type of the method that is the stack boundary into the logging system for this call" and is used to determine where to stop recording the stack trace - which is required internally, or else the stack trace would include log4net internal methods.

You can certainly create an extension method that passes null as the callerStackBoundaryDeclaringType, as it will internally default to typeof(Logger).

0
votes

You should not create an extension method like you suggested.

Because ILog Logger is static it does not make much sense to use it as a paramter value. Just use Log4Net the following way:

private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); // that part was ok

public static void Trace(object message, Exception e)
{
   Logger.DebugFormat(message, e); // Use InfoFormat or something else if needed
}

if you use just standard functionality, just call DebugFormat() (or something else) directly:

try {
  int i=7/0;
} catch (Exception e){
  Logger.ErrorFormat("Looks like you are not Jon Skeet",e); 
}

It does not make sense to create your own "Trace"-Method if you add nothing to it.

In addition it (usually) does not make sense to set the type when You log something, but ONLY on top of the class when initializig Logger