Can NLog preserve callsite information through c# extension methods?

后端 未结 3 743
南方客
南方客 2021-01-27 19:37

EDIT: While similar, this is not the same as the questions about using an NLog wrapper. Extension methods add another level of indirection which makes even a proper wra

相关标签:
3条回答
  • 2021-01-27 20:06

    EDIT: Unfortunately, this answer no longer works. NLog broke this functionality in 3.2.0 and it looks like they don't plan to fix it: https://github.com/NLog/NLog/issues/696.

    I found a workaround. While not identical to the solution for just an NLog wrapper, it turns out to be similar.

    Instead of having the ILogger implementer (the NLog wrapper) simply pass it's own type to NLog, I created an overload that allowed passing it a type from the caller:

    public void Log( LogEntry entry )
    {
        this.Log( this.GetType(), entry );
    }
    
    public void Log( Type type, LogEntry entry)
    {
        NLogLogger.Log( type, new NLog.LogEventInfo( ... ) );
    }
    

    This requires adding the overload to interface, which makes it sort of ugly (and NLog specific):

    public interface ILogger
    {
        void Log( LogEntry entry );
        void Log( Type type, LogEntry entry );
    }
    

    And then the extension methods can be modified:

    public static class LoggerExtensions
    {
        public static void Debug( this ILogger logger, string format params object[] args )
        {
            logger.Log( typeof(LoggerExtensions), LogLevel.Debug, format, args ) );
        }
    
        ...
    }
    

    While not as clean as simply using a wrapper, this does allow the use of extension methods while retaining callsite information. If anyone has a cleaner way I'd like to see it.

    0 讨论(0)
  • 2021-01-27 20:08

    Is LogEntry your own class/struct? Maybe you could add a field/property to it to hold the logger type. In your extension method, when you create a LogEntry to send to your ILogger, populate the LogEntry.LoggerType with tyepof(LoggerExtensions).

    So, your LoggerExtensions class might look something like this (uncompiled and untested):

    public static class LoggerExtensions
    {
        public static void Debug( this ILogger logger, string format, params object[] args)
        {
            logger.Log( new LogEntry( typeof(LoggerExtensions), LogLevel.Debug, format, args ) );
        }
    
        ...
    }
    

    Since log4net uses a similar scheme (of looking at the logger type to tell which stack frame corresponds to the actual call site), you could write a log4net wrapper and corresponding log4net wrapper extension methods, if you were so inclined.

    0 讨论(0)
  • 2021-01-27 20:10

    Late to the party but I had issue with this solution given that I use extension methods for my wrapper. By passing the assembly to LogManager I was able to get NLog to ignore my extension class.

        /// <summary>
        /// Bootstrap the wrapper class
        /// </summary>
        static Logger()
        {
            LogManager.AddHiddenAssembly(typeof(LoggingExtensions).Assembly);
        }
    

    There isn't much detail from the docs other than

    Adds the given assembly which will be skipped when NLog is trying to find the calling method on stack trace.

    from NLog Documentation

    With this setup, I've even managed to get extension methods + DI working with SimpleInjector.

    To show you can still have a callsite within the same assembly using this method

    My Logger() lives in a Utilities project along with a SettingsHelper() I setup a test with output from SettingsHelper:

    2015-08-18 20:44:07.5352 | vstest.executionengine.x86 | Debug | Utilities.Settings.SettingsHelper | A test from the same assembly as Logger() | ActionTests.LoggingTest+LogTest.RunLogTest

    The bold bit is the ${callsite}

    My SettingsHelper() test:

    ILogger logger = new Logger(typeof(SettingsHelper));
    logger.Debug("A test from the same assembly as Logger()");
    

    Don't forget also to use the overload that takes LogEventInfo()

    _logger.Log(typeof(Logger), logEvent);
    
    0 讨论(0)
提交回复
热议问题