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

8
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>

这段代码假设Error总是伴随异常而来,而Warning则不是,但这不是重点。
问题在于此配置是错误的。它不能工作,因为logger没有layout属性。它只被定义为target正在使用的布局必须由目标自己声明,但我看不到指定不同严重级别的不同布局的方法。 现在,我不得不复制粘贴相同的配置代码四次,以便为相同的一组文件拥有四个不同的layout
<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>

这让我眼睛疼。
有没有更好的方法来避免重复?

3个回答

18

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

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

我希望在出现错误时提供完整的堆栈跟踪信息,而在非错误情况下仅提供异常消息。


1
这应该是被接受的答案。如果我正确理解了OP,那么问题应该重新表述以特别解决这个问题:“我还想为不同的严重程度使用不同的布局”,然后这将是针对该问题的适当答案。 - MattM

1

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

根据你想要通过格式实现的确切目标,你可能可以通过编写自定义LayoutRenderer来减少重复。在你的示例中,你展示了Debug布局中有“...”,Info布局中有[i],Warn布局中有[!],而Error布局中有Warn + exception。你可以编写一个LayoutRenderer,根据消息级别添加特殊标记。这样,你就可以将Debug、Info和Warn全部合并到一个布局中,而Error则保留自己的布局。

例如:

像这样为自定义LayoutRenderer(基于NLog 1.0 refresh,而不是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;     
    }
  } 

现在您可以配置两个布局: "normal" 和 "error"。

类似于:

<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,因为没有异常出现。
希望这可以帮到你。

感谢您的评论。目前我想尽量减少依赖,但当我重新设计系统时,也许会考虑实现这个功能。 - Dan Abramov

0
如果您想根据日志级别(或其他条件)使用不同的NLog布局,则可以使用NLog条件:
<nlog throwConfigExceptions="true">
  <variable name="infoLayout" value="${longdate} ${logger} ${message}" />
  <variable name="errorLayout" value="${longdate} ${logger} ${message} ${exception}" />
  <variable name="defaultLayout" value="${when:when=level<=LogLevel.Info:inner=${infoLayout}:else=${errorLayout}}" />
    
  <targets>
    <target xsi:type="file" name="logfile" fileName="App.Log" layout="${defaultLayout}" />
  </target>

  <rules>
    <logger name="*" minlevel="Debug" writeTo="logfile"  />
  </rules>
</nlog>

参见:https://github.com/NLog/NLog/wiki/When-Layout-Renderer 参见:https://github.com/NLog/NLog/wiki/OnException-Layout-Renderer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接