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?
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>
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.
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"
/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With