Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ILogger.Log method declaringType parameter

Tags:

c#

log4net

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?

like image 525
Can Bud Avatar asked May 29 '15 08:05

Can Bud


1 Answers

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).

like image 101
stuartd Avatar answered Nov 20 '22 10:11

stuartd