Question

When I use %l or %L in my PatternLayout for debugging, I get the location of my extension static class and the line number in that file and not the location and line of the caller. Any of the standard logging methods produce correct results. Is there a way to get the extension methods to do this?

Part of my Log4NetExtensions.cs

namespace Dashboard
{
    public static partial class Util
    {
        public static void SqlError(this ILog log, SqlException sqle)
        {
             if (log.IsDebugEnabled)
            {
                string[] names = Enum.GetNames(typeof(Dashboard.Models.Response.DashError.StandardErrors));
                int idx = Array.IndexOf(names, sqle.Message);
                if (idx > 0)
                {
                    log.Debug(sqle.Message);
                }
                else
                {
                    log.Error(sqle);
                }
            }
            else
            {
                log.Error(sqle);
            }
        }
    }
}

Edit: Per wageoghe's answer, I changed the log.Error() and log.Debug to this but it still prints Util and not the caller:

log.Logger.Log(typeof(Util), Level.Error, sqle.Message, sqle);
Was it helpful?

Solution

See this answer to another question here on SO for how to maintain call site information when wrapping log4net.

how to log method name when using wrapper class with Log4net

Even though you are writing an extension method, you are essentially wrapping log4net. The technique described in that answer should work for an extension method as well as it does for a wrapper. The solution for you should be to use the Log method rather than Info, Error, etc inside your wrapper. As the first parameter to the Log method, send the type of your extension method static class.

Your extension method will look something like this (not compiled or tested):

namespace Dashboard
{
    public static partial class Util
    {
        public static void SqlError(this ILog log, SqlException sqle)
        {
             if (log.IsDebugEnabled)
            {
                string[] names = Enum.GetNames(typeof(Dashboard.Models.Response.DashError.StandardErrors));
                int idx = Array.IndexOf(names, sqle.Message);
                if (idx > 0)
                {
                    //Note that I am using the Logger member and then calling the Log method it.
                    log.Logger.Log(typeof(Util), LogLevel.Debug, sqle.Message, null);
                }
                else
                {
                    //Not sure about this call because you want to log only the exception.  Don't
                    //know if log4net will accept null as "message" parameter.
                    log.Logger.Log(typeof(Util), LogLevel.Error, null, sqle);
                }
            }
            else
            {
                //Not sure about this call because you want to log only the exception.  Don't
                //know if log4net will accept null as "message" parameter.
                log.Logger.Log(typeof(Util), LogLevel.Error, null, sqle);
            }
        }
    }
}

If it doesn't work exactly right, I think you will get the idea.

UPDATE

FWIW, I put together this little sample and it logs the desired method (Main) for the call site using an extension method:

  class Program
  {
    static void Main(string[] args)
    {
      var logger = LogManager.GetLogger("abc");
      ILogger ilog = logger.Logger;

      logger.Info("Hello");
      logger.InfoExt("Hello2");
    }
  }

  public static class Extensions
  {
    public static void InfoExt(this ILog logger, string message)
    {
      logger.Logger.Log(typeof(Extensions), Level.Info, message, null);
    }
  }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top