如何在NLog中将不同的布局应用于同一目标?

问题描述 投票:6回答:2

NLog允许我使用SplitGroup将我的消息记录到多个目标。我想使用此功能一次将每条消息记录到常见的,特定于用户和日期的日志中:

<variable name="commonLog" value="${logDir}\Common.log" />
<variable name="username" value="${identity:fSNormalize=true:authType=false:isAuthenticated=false}" />
<variable name="userLog" value="${logDir}\ByUser\${username}.log" />
<variable name="dateLog" value="${logDir}\ByDate\${shortdate}.log" />

<target name="logFiles" xsi:type="SplitGroup">
  <target xsi:type="File" fileName="${commonLog}" layout="${myLayout}" />
  <target xsi:type="File" fileName="${userLog}" layout="${myLayout}" />
  <target xsi:type="File" fileName="${dateLog}" layout="${myLayout}" />
</target>

这很好,但我也想对不同级别的严重性使用不同的布局。例如,errorLayout将包含异常信息并插入[!]标记,以便稍后我可以突出显示BareTail等日志查看器中的错误:

<variable name="stamp" value="${date} ${username} ${logger}" />

<variable name="debugLayout" value="${stamp} ... ${message}" />
<variable name="infoLayout" value="${stamp} [i] ${message}" /> 
<variable name="warnLayout" value="${stamp} [!] ${message}" />
<variable name="errorLayout"
   value="${warnLayout}${newline}${pad:padding=10:inner=${exception:format=ToString}}" />

<!-- logFiles target -->

<rules>
  <logger name="*" level="Debug" writeTo="logFiles" layout="debugLayout"  />
  <logger name="*" level="Info" writeTo="logFiles" layout="infoLayout" />
  <logger name="*" level="Warn" writeTo="logFiles" layout="warnLayout" />
  <logger name="*" level="Error" writeTo="logFiles" layout="errorLayout" />
</rules>

这段代码假设Errors总是带有异常,而Warnings则没有,但这不是重点。

问题是这种配置是错误的。它不起作用,因为logger没有layout属性。它仅针对target定义。

正在使用的布局必须由目标本身声明,但我认为无法为不同的严重性级别指定不同的布局。

现在,我不得不复制粘贴相同的配置代码四次只是为同一组文件有四个不同的layouts:

<targets>
  <target name="logFilesDebug" xsi:type="SplitGroup">
    <target xsi:type="File" fileName="${commonLog}" layout="${debugLayout}" />
    <target xsi:type="File" fileName="${userLog}" layout="${debugLayout}" />
    <target xsi:type="File" fileName="${dateLog}" layout="${debugLayout}" />
  </target>

  <target name="logFilesInfo" xsi:type="SplitGroup">
    <target xsi:type="File" fileName="${commonLog}" layout="${infoLayout}" />
    <target xsi:type="File" fileName="${userLog}" layout="${infoLayout}" />
    <target xsi:type="File" fileName="${dateLog}" layout="${infoLayout}" />
  </target>

  <target name="logFilesWarn" xsi:type="SplitGroup">
    <target xsi:type="File" fileName="${commonLog}" layout="${warnLayout}" />
    <target xsi:type="File" fileName="${userLog}" layout="${warnLayout}" />
    <target xsi:type="File" fileName="${dateLog}" layout="${warnLayout}" />
  </target>

  <target name="logFilesError" xsi:type="SplitGroup">
    <target xsi:type="File" fileName="${commonLog}" layout="${errorLayout}" />
    <target xsi:type="File" fileName="${userLog}" layout="${errorLayout}" />
    <target xsi:type="File" fileName="${dateLog}" layout="${errorLayout}" />
  </target>
</targets>

<rules>
  <logger name="*" level="Debug" writeTo="logFilesDebug"  />
  <logger name="*" level="Info" writeTo="logFilesInfo" />
  <logger name="*" level="Warn" writeTo="logFilesWarn" />
  <logger name="*" level="Error" writeTo="logFilesError" />
</rules>

这只是伤害了我的眼睛。 有没有更好的方法来做到这一点,避免重复?

c# configuration nlog redundancy nlog-configuration
2个回答
1
投票

我不确定,但我认为你可能会遇到重复问题。您希望在同一文件上使用4种不同的布局,并且需要3种不同的文件。一个目标需要一个布局。因此,如果您只想记录到1个文件,则仍需要定义4个目标,每个目标指向同一个文件,每个目标都有自己的布局。我不认为NLog有更方便的方法将多个布局与目标关联,然后根据日志消息的内容选择一个布局。

根据您希望通过格式实现的目标,您可以通过编写自定义LayoutRenderer来减少重复。在您的示例中,您显示Debug布局中包含“...”,Info具有[i],Warn具有[!],并且Error具有警告+异常。您可以编写一个LayoutRenderer来添加特殊标记,具体取决于消息的级别。这样,您可以将Debug,Info和Warn all滚动到一个Layout中,Error将保留自己的Layout。

例如:

对于自定义LayoutRenderer(基于NLog 1.0刷新,而不是2.0),这样的事情:

  [LayoutRenderer("LevelMarkerLayoutRenderer")]   
  class LevelMarkerLayoutRenderer : LayoutRenderer   
  {     
    int estimatedSize = 3;      
    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {       
      string marker;
      switch (logEvent.Level)
      {
        case Debug:
          marker = "...";
          break;
        case Info:
          marker = "[i]";
          break;
        case Warn:
          marker = "[!]";
          break;
        case Error:
          marker = "[!]";
          break;
        case Fatal:
          marker = "[!]";
          break;
        default:
          marker = "?";
      }

      builder.Append(marker);     
    }      

    protected override int GetEstimatedBufferSize(LogEventInfo logEvent)     
    {       
      return estimatedSize;     
    }
  } 

现在您可以配置两个布局:“正常”和“错误”。

就像是:

<variable name="stamp" value="${date} ${username} ${logger}" />

<variable name="normal" value="${stamp} ${LevelMarkerLayoutRenderer} ${message}" />
<variable name="error"
   value="${warnLayout}${newline}${pad:padding=10:inner=${exception:format=ToString}}" />

您甚至可以创建自定义LayoutRenderer来处理异常。如果没有异常,请不要输出任何内容。如果是异常,则连接换行符,填充和异常字符串。

如果您有一个“条件”异常布局渲染器,那么您可能只有一个布局可能如下所示:

<variable name="normal" value="${stamp} ${LevelMarkerLayoutRenderer} ${message} ${ConditionalExceptionLayoutRenderer}" />

大多数情况下,ConditionalExceptionLayoutRenderer将产生null,因为不会有异常。

希望这可以帮助。


12
投票

另一种解决方案是在布局中使用when条件。

target.Layout = "${longdate}|[${level}]|${logger}|${message}${onexception:inner=|${exception}${when:when=(level > LogLevel.Warn):inner=|[!] ${exception:format=ToString:innerFormat=Message:maxInnerExceptionLevel=5} }}"

我想只在错误发生时提供异常消息。当出现错误时,我想要完整的堆栈跟踪。

© www.soinside.com 2019 - 2024. All rights reserved.