如何在 NLog 中修改 ${stacktrace} 布局渲染器

本文关键字:stacktrace 布局 NLog 修改 | 更新日期: 2023-09-27 18:32:49

我开始使用NLog.Interface,以便我可以使用ILogger接口而不是NLog上提供的Logger类(主要用于单元测试

当我使用${stacktrace}时,我得到这样的东西:

... => LoginViewModel.LogIn => LoggerAdapter.Info

我想删除LoggerAdapter.Info部分(包含该部分是因为NLog.Interface wrapps LoggerAdapter 中的Logger类)。

我该怎么做?

如何在 NLog 中修改 ${stacktrace} 布局渲染器

您可以创建自定义 LayoutRenderer,它将跳过一些堆栈帧:

[LayoutRenderer("stacktrace")]
public class CustomStackTraseLayoutRenderer : StackTraceLayoutRenderer
{
   [DefaultValue(0)]
   public int SkipFrames { get; set; } // configurable
   protected override void Append(StringBuilder builder, LogEventInfo logEvent)
   {
       int startingFrame = logEvent.UserStackFrameNumber + TopFrames - 1;
       if (startingFrame >= logEvent.StackTrace.FrameCount)
           startingFrame = logEvent.StackTrace.FrameCount - 1;
        bool first = true;
        int endingFrame = logEvent.UserStackFrameNumber + SkipFrames;
        for (int i = startingFrame; i >= endingFrame; --i)
        {
            StackFrame f = logEvent.StackTrace.GetFrame(i);
            switch (Format)
            {
                case StackTraceFormat.Raw:
                    builder.Append(f.ToString());
                    break;
                case StackTraceFormat.Flat:
                    if (!first)
                        builder.Append(this.Separator);
                    var type = f.GetMethod().DeclaringType;
                    builder.Append(type == null ? "<no type>" : type.Name);
                    builder.Append(".");
                    builder.Append(f.GetMethod().Name);
                    first = false;
                    break;
                case StackTraceFormat.DetailedFlat:
                    if (!first)
                        builder.Append(this.Separator);
                    builder.Append("[" + f.GetMethod() + "]");
                    first = false;
                    break;
            }
        }
    }
}

添加自定义布局渲染器:

<extensions>
  <add prefix="custom" assembly="YourAssemblyName"/>
</extensions>
<targets>
  <target xsi:type="ColoredConsole" name="c"
          layout="${time} ${custom.stacktrace:skipframes=1} ${level} ${message}">
  </target>
</targets>

有关包装 NLog 记录器以保留调用站点信息的正确方法的简短示例,请参阅此处的 SO 答案:

匹配特定 NLog 记录器名称时出现问题

这是另一个涉及包装 NLog 记录器的问题。 实际上,问题不是关于包装 NLog 记录器,但我的答案指出了另一个答案对 NLog 记录器包装器的实现中的问题。

将 NLog 与 MEF 一起使用的最佳方法是什么?

关键是用 NLog Logger.Log 方法实现包装器的日志记录方法,并将包装器的type作为第一个参数传递。

为了节省一些时间,我在这里发布了一个稍微缩短的NLog Logger包装器代码版本:

  class NLogLogger : ILogger
  {
    private NLog.Logger logger;
    public NLogLogger(Type t)
    {
      logger = NLog.LogManager.GetLogger(t.FullName);
    }
    //Trace, Warn, Error, Fatal eliminated for brevity
    public bool IsInfoEnabled
    {
      get { return logger.IsInfoEnabled; }
    }
    public bool IsDebugEnabled
    {
      get { return logger.IsDebugEnabled; }
    }
    public void Info(string format, params object [] args)
    {
      if (logger.IsInfoEnabled)
      {
        Write(LogLevel.Info, format, args);
      }
    }
    public void Debug(string format, params object [] args)
    {
      if (logger.IsDebugEnabled)
      {
        Write(LogLevel.Debug, format, args);
      }
    }
    private void Write(LogLevel level, string format, params object [] args)
    {
      LogEventInfo le = new LogEventInfo(level, logger.Name, null, format, args);
      logger.Log(typeof(NLogLogger), le);
    }
  }

您将像这样使用包装器:

class MyClass
{
  private static readonly logger = new NLogLogger(typeof(MyClass));
  public void DoSomething()
  {
    logger.Info("Hello from DoSomething");
  }
}

或者,您可以将记录器注入到类中。