我正在编写一个自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()
,然后要么始终生成堆栈跟踪,要么从不生成堆栈跟踪。它是否正确?是否可以在事件级别自定义堆栈跟踪生成?
答案 0 :(得分:0)
一种解决方案是使用两个目标实例,并且仅使用logger-name来控制它是否应使用配置为写入StackTrace的目标:
<logger name="StackTraceEnabled" writeTo="target1_stacktrace" final="true">
<logger name="*" writeTo="target2_default" />
现在,NLog仅根据Logger-name决定是否应包括StackTrace。它会找到所有与记录器名称匹配的记录规则,如果其中包含一个具有堆栈跟踪的目标,则将完成捕获(独立于条件过滤器的规则)
修复NLog应该很容易,因此它也可以在尝试捕获StackTrace之前检查过滤条件(允许对除logger名称之外的其他内容进行过滤优化)。
答案 1 :(得分:0)
确认已接受的答案是什么:如果任何NLog目标要求任何消息的堆栈跟踪(与过滤器无关),并且NLog.LoggerImpl.Write
实例不需要,则LogEventInfo
内部方法将捕获堆栈跟踪已经有堆栈跟踪。根据{{3}}:
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为那些特定日志条目生成堆栈跟踪的最简单方法。