编辑:虽然类似,但这与使用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等功能的唯一方法吗?
答案 0 :(得分:8)
派对上晚了但是我对这个解决方案有疑问,因为我使用了包装器的扩展方法。通过将程序集传递给LogManager,我能够让NLog忽略我的扩展类。
/// <summary>
/// Bootstrap the wrapper class
/// </summary>
static Logger()
{
LogManager.AddHiddenAssembly(typeof(LoggingExtensions).Assembly);
}
以外的文档没有太多细节
添加给定的程序集,当NLog尝试在堆栈跟踪上查找调用方法时,将跳过该程序集。
通过这种设置,我甚至设法使用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包装器扩展方法。