NLog可以通过c#扩展方法保存呼叫站点信息吗?

时间:2013-05-03 20:00:44

标签: c# nlog

编辑:虽然类似,但这与使用NLog包装器的问题不同。扩展方法增加了另一个间接级别,这使得即使是正确的包装器也会报告错误的调用点

我目前在NLog周围使用了一个日志包装器,我使用他们在源代码中显示的技巧来获取准确的callsite信息。对于我开始的新项目,我想创建一个更简单的类,所以我只是实现了类似下面的接口:

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

然后我创建了一个扩展方法类,如:

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

    ...
}

问题是NLog然后将callsite显示为扩展方法而不是扩展方法的调用者。我做了一些搜索,但找不到任何关于NLog和扩展方法。

在这种情况下是否可以修复呼叫站点信息?或者是在接口本身中包含Debug,Info等功能的唯一方法吗?

3 个答案:

答案 0 :(得分:8)

派对上晚了但是我对这个解决方案有疑问,因为我使用了包装器的扩展方法。通过将程序集传递给LogManager,我能够让NLog忽略我的扩展类。

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

以外的文档没有太多细节
  

添加给定的程序集,当NLog尝试在堆栈跟踪上查找调用方法时,将跳过该程序集。

来自NLog Documentation

通过这种设置,我甚至设法使用SimpleInjector获得扩展方法+ DI。

要显示您仍然可以使用此方法在同一个程序集中拥有一个调用点

My Logger()与SettingsHelper()一起存在于Utilities项目中 我使用SettingsHelper的输出设置了一个测试:

  

2015-08-18 20:44:07.5352 | vstest.executionengine.x86 |调试| Utilities.Settings.SettingsHelper |来自与Logger()|相同的程序集的测试的 ActionTests.LoggingTest + LogTest.RunLogTest

粗体位是$ {callsite}

我的SettingsHelper()测试:

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

不要忘记使用LogEventInfo()

的重载
_logger.Log(typeof(Logger), logEvent);

答案 1 :(得分:1)

编辑:不幸的是,这个答案不再适用。 NLog在3.2.0中破坏了这个功能,看起来他们不打算修复它:https://github.com/NLog/NLog/issues/696

我找到了解决方法。虽然不完全相同只是一个NLog包装器的解决方案,但事实证明它是相似的。

我没有让ILogger实现者(NLog包装器)简单地将它自己的类型传递给NLog,而是创建了一个允许从调用者传递类型的重载:

public void Log( LogEntry entry )
{
    this.Log( this.GetType(), entry );
}

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

这需要在接口上添加重载,这会使它变得丑陋(并且特定于NLog):

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

然后可以修改扩展方法:

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

    ...
}

虽然不像使用包装器那样干净,但这确实允许在保留呼叫站点信息的同时使用扩展方法。如果有人有一个更清洁的方式我想看到它。

答案 2 :(得分:0)

LogEntry是你自己的类/结构吗?也许你可以添加一个字段/属性来保存记录器类型。在扩展方法中,当您创建要发送到ILogger的LogEntry时,使用tyepof(LoggerExtensions)填充LogEntry.LoggerType。

因此,您的LoggerExtensions类可能看起来像这样(未编译和未经测试):

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

    ...
}

由于log4net使用类似的方案(查看记录器类型以告知哪个堆栈帧对应于实际的调用站点),如果您愿意,可以编写log4net包装器和相应的log4net包装器扩展方法。