views:

144

answers:

1

Hi folks,

I've got a simple winform test app i'm using to try some Log4Net Dependency Injection stuff.

I've made a simple interface in my Services project :-

public interface ILogging
{
    void Debug(string message);

    // snip the other's.
}

Then my concrete type will be using Log4Net...

public class Log4NetLogging : ILogging
{
    private static ILog Log4Net
    {
        get { return LogManager.GetLogger(
                       MethodBase.GetCurrentMethod().DeclaringType); }
    }

    public void Debug(string message)
    {
        if (Log4Net.IsDebugEnabled)
        {
            Log4Net.Debug(message);
        }
    }
}

So far so good. Nothing too hard there.

Now, in a different project (and therefore namesapce), I try and use this ...

public partial class Form1 : Form
{  
    public Form1()
    {
        FileInfo fileInfo = new FileInfo("Log4Net.config");
        log4net.Config.XmlConfigurator.Configure(fileInfo);
    }

    private void Foo()
    {
        // This would be handled with DI, but i've not set it up 
        // (on the constructor, in this code example).
        ILogging logging = new Log4NetLogging(); 
        logging.Debug("Test message");
    }
}

Ok .. also pretty simple. I've hardcoded the ILogging instance but that is usually dependency injected via the constructor. Anyways, when i check this line of code...

return LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

the DeclaringType type value is of the Service namespace, not the type of the Form (ie. X.Y.Z.Form1) which actually called the method.

Without passing the type INTO method as another argument, is there anyway using reflection to figure out the real method that called it?

A: 

To get the MethodInfo object that is calling a particular method, you need to examine the call stack. This can be accomplished through the StackTrace class in the following manner.

using System.Diagnostics;
using System.Reflection;

public class Log4NetLogging : ILogging
{
    private ILog GetLogForStackFrame(StackFrame frame)
    {
        return LogManager.GetLogger(frame.GetMethod().DeclaringType);
    }

    public void Debug(string message)
    {
        ILog log = GetLogForStackFrame(new StackTrace().GetFrame(0));
        if (log.IsDebugEnabled)
        {
            log.Debug(message);
        }
    }
}

On a side note, I would consider modifying your interface to accept the Type of the object that will be using the abstract log. I realize you don't want to do this, but the retrieving the call stack at runtime is a costly operation and will need to be performed each time you call ILogging.Debug.

Steve Guidi
cheers, I'll go and try this out. I was afraid you were going to say that this reflection was expensive. On a side note, what exactly does 'GetLogger(string)' do ... does it look for a config entry value => <logger name="XXXX"> ... </> .. so in theory, i could just pass in some manual string here .. and make sure that exists in the config file?
Pure.Krome
The documentation on `GetLogger(string)` isn't very helpful, but it does suggest that the log configuration is retrieved from the configuration file. If this is true, then you can replace "get-logger-for-type-T" code with a "get-logger-by-name" call, as you suggested, and configure the named logger for type T in the configuration file.See http://logging.apache.org/log4net/release/sdk/log4net.LogManager.GetLogger_overload_1.html for more information.
Steve Guidi
Yep. it took me _AGES_ to get it working, but if u have <logger name="Hello">...</Logger>, then you can do GetLogger("Hello"). What took me ages to get it working, was that it's _CASE BLOODY SENSITIVE_. so i was doing GetLogger("hello") and that was returning the default/root logger :( Damn it! :X
Pure.Krome