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

编辑:虽然类似,但这与使用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然后将调用点显示为扩展方法而不是扩展方法的调用者。 我做了一些搜索,但找不到任何关于NLog和扩展方法。

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

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

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

除了以外的文档没有太多细节

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

来自NLog文档

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

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

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

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

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

而不是让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 ) ); } ... } 

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

LogEntry是你自己的类/结构吗? 也许你可以添加一个字段/属性来保存记录器类型。 在扩展方法中,当您创建LogEntry以发送到ILogger时,使用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包装器扩展方法。