Nlog - 为日志文件生成标题部分

时间:2010-11-16 16:51:32

标签: c# logging nlog

刚刚开始尝试使用NLog,我发现我希望能够将标题信息添加到日志文件的顶部,例如:

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

经过一些搜索后,我无法在现有的在线文档或代码论坛中找到任何表明此类功能的内容。这可能吗?我以前总是将这种信息包含在日志文件中,并且发现它在过去的许多事件中都很有用,当时在客户站点获取有关生产问题的信息。不可否认,此功能是为解决方案定制的,而不是基于任何当前的.NET日志框架。

3 个答案:

答案 0 :(得分:13)

偶然发现在看到我用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

-Jody

答案 1 :(得分:4)

我不知道如何轻松地做到这一点。话虽如此,您提供的所有示例都可用(或非常容易使用某些自定义代码)添加到每条日志消息中。也就是说,每个记录的消息都可以通过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”与“HeaderLayout”相关联的“fileHeader”目标相关联。标题布局中定义的字段将写入日志文件。子序列日志消息,因为它们不使用“HeaderLogger”,将使用“根”(*)布局。它们将转到同一个文件,因为“file”和“fileHeader”目标最终都指向相同的文件名。

在我开始输入此响应之前,我不确定您是否可以轻松地在日志文件中添加标头。输入后,我认为它实际上可能很简单!

祝你好运!

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

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

我为每个日志记录级别设置了一个布局,在开头添加了一个描述消息级别的文字字符串(这表示每个级别使用不同的格式)。每个布局都与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"时,错误就消失了。 祝你好运!

答案 2 :(得分:1)

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

更多细节: