自定义何时在 NLog 中捕获堆栈跟踪
Customizing when to capture stack trace in NLog
我正在编写一个派生自 TargetWithLayout
的自定义 NLog 目标,我想让它根据特定的日志事件选择性地写入堆栈跟踪。我定义了这样的嵌套布局(语法可能不正确):
Layout layout = "${when:${event-properties:StackTraceEnabled}==true:${stacktrace}}";
我会创建这样的事件:
var logEventInfo = new NLog.LogEventInfo(NLog.LogLevel.Error, "Test", "Test")
{
Properties =
{
{ "StackTraceEnabled", true },
}
};
然而,这不起作用,而且似乎违背了 NLog 的设计。从我所看到的一点点来看,NLog 会根据其注册的目标评估 GetStackTraceUsage()
,然后总是生成堆栈跟踪或从不生成。这个对吗?有没有办法在事件级别自定义堆栈跟踪生成?
一个解决方案是使用两个目标实例,并且只使用 logger-name 来控制它是否应该使用配置为写入 StackTrace 的目标:
<logger name="StackTraceEnabled" writeTo="target1_stacktrace" final="true">
<logger name="*" writeTo="target2_default" />
现在 NLog 仅根据 Logger-name 决定它是否应该包含 StackTrace(或不包含)。它会找到所有与记录器名称匹配的日志记录规则,如果其中一个包含带有堆栈跟踪的目标,则将完成捕获(独立于具有条件过滤器的规则)
修复 NLog 应该很容易,因此它还会在尝试捕获 StackTrace 之前检查过滤条件(允许对 logger-name 以外的其他内容进行过滤优化)。
确认已接受的答案所述:如果任何 NLog 目标需要任何消息的堆栈跟踪(不考虑过滤器),并且 LogEventInfo
实例,NLog.LoggerImpl.Write
内部方法将捕获堆栈跟踪还没有堆栈跟踪。每 LoggerImpl.cs:
internal static class LoggerImpl
{
internal static void Write([NotNull] Type loggerType, TargetWithFilterChain targets, LogEventInfo logEvent, LogFactory factory)
{
if (targets == null)
return;
StackTraceUsage stu = targets.GetStackTraceUsage();
if (stu != StackTraceUsage.None && !logEvent.HasStackTrace)
{
var stackTrace = new StackTrace(StackTraceSkipMethods, stu == StackTraceUsage.WithSource);
var stackFrames = stackTrace.GetFrames();
int? firstUserFrame = FindCallingMethodOnStackTrace(stackFrames, loggerType);
int? firstLegacyUserFrame = firstUserFrame.HasValue ? SkipToUserStackFrameLegacy(stackFrames, firstUserFrame.Value) : (int?)null;
logEvent.GetCallSiteInformationInternal().SetStackTrace(stackTrace, firstUserFrame ?? 0, firstLegacyUserFrame);
}
// ...
}
}
如果已经明确设置,上述方法将避免捕获实际的堆栈跟踪,即使只是一个占位符。因此,将堆栈跟踪显式设置为占位符是防止 NLog 为那些特定日志条目生成堆栈跟踪的最简单方法。
我正在编写一个派生自 TargetWithLayout
的自定义 NLog 目标,我想让它根据特定的日志事件选择性地写入堆栈跟踪。我定义了这样的嵌套布局(语法可能不正确):
Layout layout = "${when:${event-properties:StackTraceEnabled}==true:${stacktrace}}";
我会创建这样的事件:
var logEventInfo = new NLog.LogEventInfo(NLog.LogLevel.Error, "Test", "Test")
{
Properties =
{
{ "StackTraceEnabled", true },
}
};
然而,这不起作用,而且似乎违背了 NLog 的设计。从我所看到的一点点来看,NLog 会根据其注册的目标评估 GetStackTraceUsage()
,然后总是生成堆栈跟踪或从不生成。这个对吗?有没有办法在事件级别自定义堆栈跟踪生成?
一个解决方案是使用两个目标实例,并且只使用 logger-name 来控制它是否应该使用配置为写入 StackTrace 的目标:
<logger name="StackTraceEnabled" writeTo="target1_stacktrace" final="true">
<logger name="*" writeTo="target2_default" />
现在 NLog 仅根据 Logger-name 决定它是否应该包含 StackTrace(或不包含)。它会找到所有与记录器名称匹配的日志记录规则,如果其中一个包含带有堆栈跟踪的目标,则将完成捕获(独立于具有条件过滤器的规则)
修复 NLog 应该很容易,因此它还会在尝试捕获 StackTrace 之前检查过滤条件(允许对 logger-name 以外的其他内容进行过滤优化)。
确认已接受的答案所述:如果任何 NLog 目标需要任何消息的堆栈跟踪(不考虑过滤器),并且 LogEventInfo
实例,NLog.LoggerImpl.Write
内部方法将捕获堆栈跟踪还没有堆栈跟踪。每 LoggerImpl.cs:
internal static class LoggerImpl
{
internal static void Write([NotNull] Type loggerType, TargetWithFilterChain targets, LogEventInfo logEvent, LogFactory factory)
{
if (targets == null)
return;
StackTraceUsage stu = targets.GetStackTraceUsage();
if (stu != StackTraceUsage.None && !logEvent.HasStackTrace)
{
var stackTrace = new StackTrace(StackTraceSkipMethods, stu == StackTraceUsage.WithSource);
var stackFrames = stackTrace.GetFrames();
int? firstUserFrame = FindCallingMethodOnStackTrace(stackFrames, loggerType);
int? firstLegacyUserFrame = firstUserFrame.HasValue ? SkipToUserStackFrameLegacy(stackFrames, firstUserFrame.Value) : (int?)null;
logEvent.GetCallSiteInformationInternal().SetStackTrace(stackTrace, firstUserFrame ?? 0, firstLegacyUserFrame);
}
// ...
}
}
如果已经明确设置,上述方法将避免捕获实际的堆栈跟踪,即使只是一个占位符。因此,将堆栈跟踪显式设置为占位符是防止 NLog 为那些特定日志条目生成堆栈跟踪的最简单方法。