我如何操作 NLog 的堆栈级别?

How do i manipulate the stack level of NLog?

我有一个扩展方法来支持像这样的额外日志级别:

    public static void Duration(this ILogger source, string message)
    {
        var logEventInfo = new LogEventInfo(LogLevel.Warn, source.Name, message);
        logEventInfo.Properties.Add("specialLevel", "Duration");
        source.Log(logEventInfo);
    }

这里有一个问题。

我的 ${callsite} 现在总是报告持续时间。有什么方法可以将调用堆栈级别增加一级,以获取调用 Duration 的方法,或者我是否需要为此自定义渲染器?

到目前为止我尝试了什么:

logEventInfo.Properties.Add("skipFrames", 1);

layout="${formattedDate} ${callsite:skipFrames=${event-properties:item=skipFrames}} ${message}"

丑陋的解决方法:

[LayoutRenderer("customCallSite")]
public class CustomCallSiteLayoutRenderer : CallSiteLayoutRenderer
{
    private readonly CallSiteLayoutRenderer DefaultRenderer = new CallSiteLayoutRenderer();

    /// <inheritdoc />
    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
        if (!logEvent.HasProperties)
        {
            builder.Append(DefaultRenderer.Render(logEvent));
            return;
        }

        if (logEvent.Properties["skipFrames"] is int skipFrames)
        {
            builder.Append(GetRenderer(skipFrames).Render(logEvent));
        }
        else
        {
            builder.Append(logEvent.Level);
        }
    }

    private static readonly ConcurrentDictionary<int, CallSiteLayoutRenderer> Renderers = new ConcurrentDictionary<int, CallSiteLayoutRenderer>();

    private CallSiteLayoutRenderer GetRenderer(int skipFrames)
    {
        return Renderers.GetOrAdd(skipFrames, f => new CallSiteLayoutRenderer() {SkipFrames = skipFrames});
    }
}

您可以采用另一种方法

  1. 将 "Duration" 方法移动到新程序集
  2. 通过调用 AddHiddenAssembly

    在堆栈跟踪中隐藏该程序集
    LogManager.AddHiddenAssembly(yourAssemblyWithDurationMethod)
    

然后不需要自定义布局渲染器:)

PS:您的 "Duration" 方法应命名为 "LogDuration"。 ;) 从 API 的角度来看,我认为这是一种有点奇怪的方法。