0

编辑:虽然类似,但这与使用 NLog 包装器的问题不同。扩展方法添加了另一层间接性,即使是适当的包装器也会报告错误的调用点

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

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 然后将调用站点显示为扩展方法,而不是扩展方法的调用者。我做了一些搜索,但找不到任何关于 NLog 和扩展方法的信息。

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

4

3 回答 3

8

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

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

文档中没有太多细节,除了

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

来自NLog 文档

通过这个设置,我什至设法让扩展方法 + DI 与 SimpleInjector 一起工作。

为了表明您仍然可以使用此方法在同一程序集中拥有一个调用点

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

2015-08-18 20:44:07.5352 | vstest.executionengine.x86 | 调试 | 实用程序.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);
于 2015-08-19T12:17:19.433 回答
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 ) );
    }

    ...
}

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

于 2013-05-03T20:31:58.763 回答
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 包装器扩展方法。

于 2013-05-06T17:15:34.353 回答