【问题标题】:Nlog - Generating Header Section for a log fileNlog - 为日志文件生成标题部分
【发布时间】:2019-04-09 02:49:12
【问题描述】:

最近刚开始尝试使用 NLog,突然想到我希望能够在日志文件的顶部添加标题信息,例如:

Executable name
File version
Release Date
Windows User ID
etc...

经过一番搜索,我无法在现有的在线文档或代码论坛中找到任何表明此类功能的内容。这可能吗?我以前一直在日志文件中包含此类信息,并且发现它在过去的许多场合中很有用,在客户现场采购有关生产问题的信息时。诚然,此功能是为解决方案定制的,而不是基于任何当前的 .NET 日志框架。

【问题讨论】:

    标签: c# logging nlog


    【解决方案1】:

    我的一位同事在使用 log4net 创建的日志中查看复制页眉/页脚时偶然发现了这一点。我从一些开源项目中找到了这个并将其改编为内部示例。我认为它应该很容易根据您的需要进行修改。

    <target name="logfile2" xsi:type="File" fileName="Logs\NLogDemo2.txt">
      <layout xsi:type="LayoutWithHeaderAndFooter">
        <header xsi:type="SimpleLayout" text="----------NLog Demo Starting---------&#xD;&#xA;"/>
        <layout xsi:type="SimpleLayout" text="${longdate}|${level:uppercase=true}|${logger}|${message}" />
        <footer xsi:type="SimpleLayout" text="----------NLog Demo Ending-----------&#xD;&#xA;"/>
      </layout>
    </target>
    

    它给我的输出看起来像这样:

    ----------NLog Demo Starting---------
    
    2013-03-01 16:40:19.5404|INFO|Project.Form1|Sample informational message
    2013-03-01 16:40:19.5714|WARN|Project.Form1|Sample warning message
    2013-03-01 16:40:19.5714|ERROR|Project.Form1|Sample error message
    2013-03-01 16:40:19.5714|FATAL|Project.Form1|Sample fatal error message
    ----------NLog Demo Ending-----------
    

    我不知道为什么这似乎没有记录。我能找到的唯一参考是:https://github.com/nlog/NLog/wiki/LayoutWithHeaderAndFooter

    -乔迪

    【讨论】:

      【解决方案2】:

      我不知道有一种方法可以很容易地做到这一点。话虽如此,您提供的所有示例都可以添加到每个日志消息中(或者通过一些自定义代码相当容易获得)。也就是说,每条记录的消息都可以通过 Layout 和 LayoutRenderers 标记为可执行名称、文件版本、发布日期、Windows 用户 ID 等。

      这显然与只在日志文件顶部创建标头不同,因此可能对您没有用处。

      另一方面,您可以使用 Pat 的回答 in this post 中提到的技术将多个布局渲染器与同一目标相关联。您可以定义一个布局,其中包含您在标题中需要的字段,并在 FilteringWrapper 中设置过滤器以仅将该布局应用于会话的第一条消息(或者您可以使用将其添加到输出文件的其他技术只有一次)。

      使用他的 NLog.config 文件,这是您可以实现所需的一种方法。请注意,我没有尝试过,所以我不知道这个配置文件是否有效,或者如果有效,它是否会生成你想要的结果。

      <nlog xmlns="http://www.nlog-project.org/schemas/NLog.mono2.xsd" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            autoReload="true" 
            internalLogLevel="Warn" 
            internalLogFile="nlog log.log" 
            > 
          <variable name="HeaderLayout" value="${processname} ${gdc:item=version} ${gdc:item=releasedate} ${windows-identity}" /> 
          <variable name="NormalLayout" value="${longdate} ${logger} ${level} ${message} /> 
      
          <targets async="true"> 
              <target name="file" xsi:type="File" fileName="log.log" 
                      layout="${NormalLayout}"> 
              </target> 
      
              <target name="fileHeader" xsi:type="File" fileName="log.log" 
                      layout="${HeaderLayout}"> 
              </target>      
          </targets> 
      
          <rules> 
              <logger name="HeaderLogger" minlevel="Trace" writeTo="fileHeader" final="true" />           
              <logger name="*" minlevel="Trace" writeTo="file" /> 
          </rules> 
      
      </nlog> 
      

      在您的代码中,您的启动逻辑可能如下所示:

      public void Main()
      {
        AddHeaderToLogFile();
      }
      
      public void AddHeaderToLogFile()
      {
        Logger headerlogger = LogManager.GetLogger("HeaderLogger");
      
        //Use GlobalDiagnosticContext in 2.0, GDC in pre-2.0
        GlobalDiagnosticContext["releasedate"] = GetReleaseDate();    
        GlobalDiagnosticContext["version"] = GetFileVersion();     
        GlobalDiagnosticContext["someotherproperty"] = GetSomeOtherProperty();
      
        headerlogger.Info("message doesn't matter since it is not specified in the layout");
      
        //Log file should now have the header as defined by the HeaderLayout
      
        //You could remove the global properties now if you are not going to log them in any
        //more messages.
      }
      

      这里的想法是在程序启动时将文件版本、发布日期等放入 GDC。使用“HeaderLogger”记录器记录消息。该消息将使用“HeaderLayout”写入日志文件,因为“HeaderLogger”与“fileHeader”目标相关联,该目标与“HeaderLayout”相关联。标题布局中定义的字段将写入日志文件。后续日志消息,因为它们不会使用“HeaderLogger”,所以将使用“root”(*) 布局。它们将转到同一个文件,因为“file”和“fileHeader”目标最终都指向同一个文件名。

      在我开始输入此响应之前,我不确定您是否可以轻松地在日志文件中添加标头。打完这个,我觉得可能真的很简单!

      祝你好运!

      [编辑] 这样的事情可能会根据级别更改布局。在第一节中,我定义了几个变量,每个变量都定义了一个布局。在下一节中,我定义了几个目标,每个目标都使用相同的文件,但被过滤为只允许写入特定级别的消息。在最后一节中,我定义了一个规则,它将所有消息(因此是“*”记录器名称)发送到所有目标。由于每个目标都按级别过滤,“trace”目标将只写入“trace”消息等。因此,“trace”消息将使用“trace”布局编写,“debug”消息将使用“debug”编写布局等。由于所有目标最终都写入同一个文件,因此所有消息最终都会在同一个文件中。我还没有尝试过,但我认为它可能会起作用。

      <variable name="TraceLayout" value="THIS IS A TRACE: ${longdate} ${level:upperCase=true} ${message}" /> 
      <variable name="DebugLayout" value="THIS IS A DEBUG: ${longdate} ${level:upperCase=true} ${message}" /> 
      <variable name="InfoLayout" value="THIS IS AN INFO: ${longdate} ${level:upperCase=true} ${message}" /> 
      
      
      <targets async="true"> 
          <target name="fileAsTrace" xsi:type="FilteringWrapper" condition="level==LogLevel.Trace"> 
              <target xsi:type="File" fileName="log.log" layout="${TraceLayout}" /> 
          </target> 
          <target name="fileAsDebug" xsi:type="FilteringWrapper" condition="level==LogLevel.Debug"> 
              <target xsi:type="File" fileName="log.log" layout="${DebugLayout}" /> 
          </target> 
          <target name="fileAsInfo" xsi:type="FilteringWrapper" condition="level==LogLevel.Info"> 
              <target xsi:type="File" fileName="log.log" layout="${InfoLayout}" /> 
          </target>  
      </targets> 
      
      <rules> 
          <logger name="*" minlevel="Trace" writeTo="fileAsTrace, fileAsDebug, fileAsInfo" /> 
      </rules> 
      

      (请注意,我这里只包含了 3 个级别)。

      展示了如何(如果可行的话)基于关卡应用不同的布局,这似乎是一个不寻常的用例。我并不是说这是一个好主意或一个坏主意,但我不能说我真的非常看到这样做。根据您希望最终输出的具体外观,我向您展示的内容可能是实现它的最佳方式,也可能不是。也许您可以发布一些示例来说明您希望输出的外观。

      您也可以考虑接受我的原始答案,然后提出一个关于改变每个级别的输出布局的新问题,以便我们可以将该问题的讨论集中在级别/布局问题上。这是否有用取决于您。

      这行得通:

        <variable name="TraceLayout" value="This is a TRACE - ${longdate} | ${logger} | ${level} | ${message}"/>
        <variable name="DebugLayout" value="This is a DEBUG - ${longdate} | ${logger} | ${level} | ${message}"/>
        <variable name="InfoLayout" value="This is an INFO - ${longdate} | ${logger} | ${level} | ${message}"/>
        <variable name="WarnLayout" value="This is a WARN - ${longdate} | ${logger} | ${level} | ${message}"/>
        <variable name="ErrorLayout" value="This is an ERROR - ${longdate} | ${logger} | ${level} | ${message}"/>
        <variable name="FatalLayout" value="This is a FATAL - ${longdate} | ${logger} | ${level} | ${message}"/>
        <targets>
          <target name="fileAsTrace" xsi:type="FilteringWrapper" condition="level==LogLevel.Trace">
            <target xsi:type="File" fileName="xxx.log" layout="${TraceLayout}" />
          </target>
          <target name="fileAsDebug" xsi:type="FilteringWrapper" condition="level==LogLevel.Debug">
            <target xsi:type="File" fileName="xxx.log" layout="${DebugLayout}" />
          </target>
          <target name="fileAsInfo" xsi:type="FilteringWrapper" condition="level==LogLevel.Info">
            <target xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />
          </target>
          <target name="fileAsWarn" xsi:type="FilteringWrapper" condition="level==LogLevel.Warn">
            <target xsi:type="File" fileName="xxx.log" layout="${WarnLayout}" />
          </target>
          <target name="fileAsError" xsi:type="FilteringWrapper" condition="level==LogLevel.Error">
            <target xsi:type="File" fileName="xxx.log" layout="${ErrorLayout}" />
          </target>
          <target name="fileAsFatal" xsi:type="FilteringWrapper" condition="level==LogLevel.Fatal">
            <target xsi:type="File" fileName="xxx.log" layout="${FatalLayout}" />
          </target>
        </targets>
      
      
          <rules>
            <logger name="*" minlevel="Trace" writeTo="fileAsTrace,fileAsDebug,fileAsInfo,fileAsWarn,fileAsError,fileAsFatal" />
            <logger name="*" minlevel="Info" writeTo="dbg" />
          </rules>
      

      我为每个日志级别设置了一个布局,在开头添加了一个描述消息级别的文字字符串(这是为了表明每个级别使用不同的格式)。每个 Layout 都与一个 FilteringWrapper 相关联,该 FilteringWrapper 根据消息的级别进行过滤,并将通过过滤器的任何消息引导到输出文件中。每个 FilteringWrapper 都包装同一个输出文件,因此所有日志消息都将记录到同一个文件中。

      这是我用于测试的一段代码:

        logger.Trace("Trace msg");
        logger.Debug("Debug msg");
        logger.Info("Info msg");
        logger.Warn("Warn msg");
        logger.Error("Error msg");
        logger.Fatal("Fatal msg");
      

      这是输出的样子:

      This is a TRACE - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Trace | Trace msg
      This is a DEBUG - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Debug | Debug msg
      This is an INFO - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Info | Info msg
      This is a WARN - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Warn | Warn msg
      This is an ERROR - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Error | Error msg
      This is a FATAL - 2010-11-22 13:20:00.4131 | NLogTest.Form1 | Fatal | Fatal msg
      

      显然我之前的配置信息中的问题是"writeTo" 值之间的空格。我猜 NLog 对此很敏感。我有类似"writeTo=blah1, blah2, blah3". 的内容,当我将其更改为"writeTo=blah1,blah2,blah3" 时,错误消失了。 祝你好运!

      【讨论】:

      • 非常感谢 wageoghe,这很有效。但是,与往常一样,在成功使基本功能正常工作后,我现在需要对事物进行微调:-) 基本上我需要为不同的日志级别设置不同的布局,如何使布局以所选级别为条件?
      • 您希望不同的布局看起来像什么?您想在布局中为不同的日志记录级别设置不同的字段吗?无论如何,您可能可以使用 Pat 在上面的链接中显示的技术。他为他的“正常”日志消息定义了一个文件目标,并定义了一个“过滤器包装器”目标以对包含异常的消息使用不同的布局。我将尝试添加一个可能对您有所帮助的示例。
      • 使用建议的技术,如下:我在配置文件中收到以下错误:''writeTo' 属性无效....' for
      • 如果回答了您的问题,请随时接受答案和/或投票
      【解决方案3】:

      您可以为每个“实例”生成页眉/页脚部分(即应用程序第一次和应用程序最后一次写入任何给定文件时)使用布局作为indicated by previous answer

      更多细节:

      【讨论】:

      • 也许我们可以将您的答案与上一个答案合并,因为您只是在添加新链接。也可以指向github.com/NLog/NLog/issues/2119,每次应用启动和停止时,都会有人要求提供页眉和页脚。
      猜你喜欢
      • 2019-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-07
      • 2012-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多