Can NLog preserve callsite information through c# extension methods?

懵懂的女人 提交于 2019-12-04 05:35:46

问题


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 wrapper report the wrong callsite

I currently use a logging wrapper around NLog, and I use the trick they show in the example in their source for getting accurate callsite info. For a new project I started, I wanted to create a simpler class, so I just implemented something like the following interface:

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

And then I created an extension method class like:

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

    ...
}

The problem is that NLog then shows the callsite as the extension method instead of the caller of the extension method. I did a little searching around but couldn't find anything about NLog and extension methods.

Is it possible to fix the callsite information in this case? Or is the only way to include the Debug,Info, etc functions in the interface itself?


回答1:


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



回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/16366655/can-nlog-preserve-callsite-information-through-c-sharp-extension-methods

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!