9
votes

I am implementing a custom XML formatter for log4.net

public class ISDSApplicationEventsLayout : XmlLayoutBase
{
    protected override void FormatXml(...)
    {
        //Location Info 
        writer.WriteStartElement("Method");
        writer.WriteString(**loggingEvent.LocationInformation.MethodName * *);
        writer.WriteEndElement();
    }
}

The problem is ... now when I call log method from my log wrapper class... called logging

public static void logEvent(string message)
{
    log.Info(isdsLog); 
}

I get the output....

  <Method>logEvent</Method>

How is it possible to have the method name that called logEvent, rather than logEvent as the method name?

Thank you

Question Update:

If this above seems a bit complicated - what I am really asking is : How do you keep the context of the method that called the wrapping logging function in log4net...

example... method doWork()... calls -> logging wrapper --> calls log4net....

How do you make the methodname = doWork and NOT logging wrapper function....

3

3 Answers

18
votes

Actually, you can fix this easily with out-of-the-box log4net. Your wrapper can call Logger.Log and pass the type of your wrapper class as the first parameter. So, your wrapper might look something like this:

public class MyLog4NetWrapper
{
  ILog log = LogManager.GetLogger("WhateverYourLoggerNameIs");

  public void logEvent(string message) 
  {     
    log.Logger.Log(typeof(MyLog4NetWrapper), LogLevel.Info, message, null);
  } 
}

When log4net logs a message, it traverses up the call stack until it gets to the method whose declaring type is equal to the type passed in as the first parameter of the Log method. The next method up the stack is the actual call site.

As far as wrapping the log4net logger, I'm not sure that I would recommend creating a static wrapper class. The main problem with that is that you can only have a single configurable logger in your app.config file. In other words, you won't be able to independently control the logging from different parts of your code. If you have class A and class B and both use your static wrapped logger, then both classes will log at the same level. If you wanted to turn logging on for class A and off for class B, you would not be able to do so.

1
votes

I don't think you can easily fix this with out-of-the-box log4net. If we take a look at the ILog.Info method in the LogImpl class, that you are calling:

    virtual public void Info(object message) 
    {
        Logger.Log(ThisDeclaringType, m_levelInfo, message, null);
    }

When a message is logged, log4net will walk the stacktrace of the current call in order to find the method that initiated the log operation. To do this Log4net uses the "ThisDeclaringType" type as the boundary of the search, the first call "above" calls within that type is chosen as the initiating method.

In your case, the first method encountered is the logEvent method. If you dropped the logEvent wrapper and used the logging methods directly you will get the desired information.

0
votes

Wraper for log4net logger in 3 steps:

Add reference to log4net package( NugetPackage)

add your wrapper class , ensure to call log4net.Config.XmlConfigurator.Configure(); in static constructor of your wrapper class. This loads all configuration from your app.config .

add log4net configuration in your app.config

See the sample wrapper class below below.

  public class Logger : ILogger
{
    private static readonly log4net.ILog log;


    static Logger()
    {
        log = log4net.LogManager.GetLogger(System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType);
        log4net.Config.XmlConfigurator.Configure();
    }

    public void Log(LogLevel logLevel, string message, params object[] args)
    {
        switch (logLevel)
        {
            case LogLevel.DEBUG: {
                    // log.Logger.Log helps logging actual method and the class which has called this method (Log(LogLevel logLevel, string message, params object[] args))
                    log.Logger.Log(typeof(Logger), log4net.Core.Level.Debug, string.Format(message, args), null);
                    break;
                }
            case LogLevel.INFO:
                {                       
                    log.Logger.Log(typeof(Logger), log4net.Core.Level.Info, string.Format( message, args) , null);
                    break;
                }
            case LogLevel.ERROR:
                {
                    log.Logger.Log(typeof(Logger), log4net.Core.Level.Error, string.Format(message, args), null);
                    break;
                }
            case LogLevel.WARN:
                {
                    log.Logger.Log(typeof(Logger), log4net.Core.Level.Warn, string.Format(message, args), null);
                    break;
                }
        }

    }

    public void LogException(LogLevel logLevel, Exception exception)
    {
        Log(logLevel, exception.ToString());
    }
}