自定义何时在NLog中捕获堆栈跟踪

时间:2018-11-02 13:48:04

标签: c# .net logging 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(),然后要么始终生成堆栈跟踪,要么从不生成堆栈跟踪。它是否正确?是否可以在事件级别自定义堆栈跟踪生成?

2 个答案:

答案 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为那些特定日志条目生成堆栈跟踪的最简单方法。