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

发布于 2024-10-20 06:17:19 字数 4047 浏览 6 评论 0原文

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 定义。

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

现在,我必须复制粘贴相同的配置代码四次才能获得同一组文件有四种不同的布局

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

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

NLog allows me to use SplitGroup to log my messages to several targets. I'd like to use this feature to log each message to a common, user-specific and date-specific logs at once:

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

This is great, but I also want to use different layouts for different levels of severity. For example, errorLayout would include exception information and insert [!] marker so I could later highlight errors in log viewers like 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>

This code assumes Errors always come with exceptions and Warnings don't but that's not the point.

The problem is this configuration is wrong. It won't work because logger does not have layout attribute. It's defined for target only.

Layout which is being used must be declared by targets themselves but I see no means of specifying different layouts for different severity levels.

For now, I had to copy-paste the same configuration code four times just to have four different layouts for same set of files:

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

This just hurts my eyes.
Is there any better way to do this and avoid duplication?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

妄断弥空 2024-10-27 06:17:19

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

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

我只想在任何小于错误的情况下提供异常消息。当出现错误时,我想要完整的堆栈跟踪。

An alternate solution is to use the when condition in the layout.

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

I wanted to just provide the exception message when anything less than error. When there was an error I wanted full stack trace.

雪化雨蝶 2024-10-27 06:17:19

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

根据您想要通过格式实现的具体目标,您也许可以通过编写自定义 LayoutRenderer 来减少重复。在您的示例中,您显示“调试”布局中有“...”,信息有 [i],警告有 [!],错误有警告 + 异常。您可以编写一个 LayoutRenderer,根据消息的级别添加特殊标记。这样,您可以将“调试”、“信息”和“警告”全部放入一个布局中,而“错误”将保留其自己的布局。

例如:

自定义 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,因为不会出现异常。

希望这有帮助。

I'm not sure, but I think that you are probably stuck with the duplication. You want 4 different layouts to be used on the same file and you want 3 different files. One target requires one Layout. So, if you only wanted to log to 1 file, you would still have to define 4 Targets, each pointing to the same file and each with its own Layout. I don't think that NLog has a more convenient way to associate multiple Layouts with a Target and then choosing one Layout based on the content of the logging message.

Depending on exactly what you want to achieve with your formats, you might be able to reduce the duplication somewhat by writing a custom LayoutRenderer. In your example, you show that the Debug layout has "..." in it, Info has [i], Warn has [!], and Error has Warn + exception. You could write a LayoutRenderer that adds the special marker, depending on what the level of the message is. That way, you would roll Debug, Info, and Warn all into one Layout and Error would retain its own Layout.

For example:

Something like this for a custom LayoutRenderer (based on NLog 1.0 refresh, not 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;     
    }
  } 

Now you could configure two Layouts: "normal", and "error".

Something like:

<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}}" />

You could probably even create a custom LayoutRenderer to handle exceptions. If no exception, don't output anything. If exception, concatentate newline, padding, and the exception string.

If you had a "conditional" exception layout renderer, then you could have just one layout that might look like this:

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

Most of the time, ConditionalExceptionLayoutRenderer would yield null because there would not be an exception.

Hope this helps.

靖瑶 2024-10-27 06:17:19

如果您希望根据 LogLevel(或其他条件)使用不同的 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

If you want different NLog Layout depending on LogLevel (or some other condition), then you can use NLog Conditions:

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

See also: https://github.com/NLog/NLog/wiki/When-Layout-Renderer

See also: https://github.com/NLog/NLog/wiki/OnException-Layout-Renderer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文