Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nlog fixed file name per app session

I am using Nlog to log from my c# app. The following is the <targets> section from my Nlog.config:

<targets>
    <target name="logfile" xsi:type="File" fileName="..\logs\${date:format=yyyyMMdd_HHmmss}_trg.log"
    layout="${counter} | ${date:format=yyyy-MM-dd HH\:mm\:ss.ffff} | ${machinename} | ${level:uppercase=true} | ${logger:shortName=true} | ${stacktrace} | ${message:exceptionSeparator=EXCEPTION:withException=true}" 
    keepFileOpen="true"/>
</targets>

For the filename I am using ${date:format=yyyyMMdd_HHmmss}_trg.log to name the log based on when it was created. However, while my app runs, the logger creates a new log file every second. How can I force Nlog to fix the file name and create only one log per session?

like image 434
meffordm Avatar asked Jun 09 '11 13:06

meffordm


3 Answers

I don't know for sure, but my guess is that NLog checks for the existence of the log file, based on the filename property (which is dynamic since you are using the date layout renderer). So, since the filename is changing (i.e. each time the filename value is retrieved it is different (or can be different)), NLog creates a new file.

Try using the shortdate layout renderer like this:

<targets>     
  <target name="logfile" xsi:type="File"    
          fileName="..\logs\${shortdate}_trg.log"     
          layout="${counter} | ${date:format=yyyy-MM-dd HH\:mm\:ss.ffff} | ${machinename} | ${level:uppercase=true} | ${logger:shortName=true} | ${stacktrace} | ${message:exceptionSeparator=EXCEPTION:withException=true}"      
          keepFileOpen="true"/> 
</targets> 

I think you will see that your filename will not change (until midnight).

The key is that NLog will always check to see if the file exists (according to the value of the filename at the time that a log message is written) and will create the file if it does not exist yet.

Alternatively, if you want to name your log file with a more precise filename (i.e. that it was created on some date at some time), then you could store that time in the GlobalDiagnosticContext and use the gdc layout renderer to help name the file. Something like this:

//Early in your program do something like this:
NLog.GlobalDiagnosticContext["StartTime"] = DateTime.Now.ToString("yyyyMMdd_HHmmss");

In your NLog.config file, do something like this:

<targets>     
  <target name="logfile" xsi:type="File"    
          fileName="..\logs\${gdc:item=StartTime}_trg.log"     
          layout="${counter} | ${date:format=yyyy-MM-dd HH\:mm\:ss.ffff} | ${machinename} | ${level:uppercase=true} | ${logger:shortName=true} | ${stacktrace} | ${message:exceptionSeparator=EXCEPTION:withException=true}"      
          keepFileOpen="true"/> 
</targets> 

Finally, you could write a custom LayoutRenderer to populate the date/time. It would get the time once and then return the same value every time.

It would look something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;

using System.Globalization;

using NLog;
using NLog.Config;
using NLog.LayoutRenderers;

namespace MyNLogExtensions
{
  [LayoutRenderer("StartTime")]
  class StartTimeLayoutRenderer : LayoutRenderer
  {
    private DateTime start = DateTime.Now;

    public StartTimeLayoutRenderer()
    {
      this.Format = "G";
      this.Culture = CultureInfo.InvariantCulture;
    }

    //
    // In NLog 1.x, LayoutRenderer defines a Culture property.
    // In NLog 2.0, LayoutRenderer does not define a Culture property.
    //
    public CultureInfo Culture { get; set; }

    [DefaultParameter]
    public string Format { get; set; }

    protected override void Append(StringBuilder builder, LogEventInfo logEvent)
    {
      builder.Append(start.ToString(this.Format, this.Culture));
    }

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

In your NLog.config file you need something like this to tell where your extensions are:

  <extensions>
    <add assembly="MyAssembly.dll"
  </extensions>

And then your target configuration would look something like this:

<targets>     
  <target name="logfile" xsi:type="File"    
          fileName="..\logs\${StartTime:format=yyyyMMdd_HHmmss}_trg.log"     
          layout="${counter} | ${date:format=yyyy-MM-dd HH\:mm\:ss.ffff} | ${machinename} | ${level:uppercase=true} | ${logger:shortName=true} | ${stacktrace} | ${message:exceptionSeparator=EXCEPTION:withException=true}"      
          keepFileOpen="true"/> 
</targets>
like image 171
wageoghe Avatar answered Nov 15 '22 02:11

wageoghe


Apparently there is a ${cached} layout renderer that will render the layout once and reuse it. https://github.com/nlog/nlog/wiki/Cached-Layout-Renderer

However, thanks to @wageoghe for your input. Your solution using the GlobalDiagnosticContext got me thinking about passing other values to NLog.config.

like image 42
meffordm Avatar answered Nov 15 '22 01:11

meffordm


Example on using ${cached} wrapper (https://github.com/nlog/NLog/wiki/Cached-Layout-Renderer) to create per-application-session log file:

<target 
    name="logfile" 
    xsi:type="File" 
    fileName="log-${date:cached=True:format=yyyy-MM-dd HH-mm-ss-fff}.txt"
/>
like image 4
lxa Avatar answered Nov 15 '22 01:11

lxa