17

I'm using NLog to do some logging and I've run into an issue with the archiving and filenames.

I'm creating the logging configurations in code (I'm writing a wrapper to expose some specific functionality), and I've got the FileTarget object created with these options:

this._fileTarget.FileName = "${date:format=yyyy-MM-dd hh.mm.ss}.log";
this._fileTarget.ArchiveAboveSize = Math.Pow(1024, 2) * 5; //5MB
this._fileTarget.ArchiveNumbering = ArchiveNumberingMode.Date;
this._fileTarget.ArchiveEvery = FileArchivePeriod.Day;
this._fileTarget.ArchiveDateFormat = "yyyy-MM-dd hh.mm.ss";

I'm not sure why NLog did it like this, but that file name layout will create a new log file every second instead of just using the file name until the archive period is up.

I added the cached target wrapper to the filename as such:

this._fileTarget.FileName = "${cached:cached=true:inner=${date:format=yyyy-MM-dd hh.mm.ss}.log}";

But all that buys me is a file name that now never changes, and looks like this when archived

2014-12-03 12.00.00.2014-12-03 12.00.00.log
2014-12-03 12.00.00.2014-12-03 12.10.00.log
etc...

Is it possible to tell NLog to only archive files based on the file size and date and to ignore the archive numbering and date formatting? I'm trying to produce log files whose name is the timestamp of when it was created, where new files are only created above a certain size or every day.

Edit: This is what I am trying to acheive:

On application start

  1. Creates log file with the file name in the format of "yyyy-MM-dd hh.mm.ss".txt by using the current time stamp (Example -> "2014-04-29 11:11:11.txt")
  2. The application logs to that file for a while until it either
    • Becomes too large (over a defined limit) or
    • The archive time elapses (in my case, this is one day)
  3. The logger then stops writing to the file created in 1. ("2014-04-29 11.11.11.txt") and does not rename it or otherwise add sequential/rolling numbers or date formatting to it.
  4. Repeat steps 1-3 until the end of time.

Leaving me with a Log folder that looks like this:

Logs
|
|> 2014-12-10 12:50:50.txt    (1024 kb)
|> 2014-12-10 12:50:55.txt    (1020 kb)
|> 2014-12-10 12:51:01.txt    (1024 kb)
|> 2014-12-10 12:51:10.txt    (1003 kb)  
|> 2014-12-10 12:51:20.txt    (400 kb)  <-The currently active log file.
Brandon
  • 4,491
  • 6
  • 38
  • 59

3 Answers3

10

This is an old question, but there are some recent improvements for this case.

I'm not sure why NLog did it like this, but that file name layout will create a new log file every second instead of just using the file name until the archive period is up.

Since NLog 4.3.9 you could configure the "cachekey", so you could control when the cache should be invalided.

For this case the following configration give you the date and time in the file name, but only 1 file each day. The time in the file name will the time of the first log event for that day.

filename="${cached:cached=true:Inner=${date:format=yyyy-MM-dd hh.mm.ss}:CacheKey=${shortdate}}.log"
Julian
  • 33,915
  • 22
  • 119
  • 174
  • 2
    This is a GREAT answer and solved my identical problem beautifully! So nice to get all the log output for a run in a SINGLE LOG FILE instead of spread out in separate files over each minute of the run. In my case the file config looked like this: fileName="\\MyOrgData2Server${var:runEnvironmentDNSSuffix}.my.org.net\Data2\myorg\MyApp\MyAppImagingTransactions\Logs\MyAppReleaseImages_${cached:cached=true:Inner=${date:format=yyyy-MM-dd hh.mm.ss}:CacheKey=${shortdate}}_${var:machineName}.log" /> Note that I am also using NLog variables in this for the environment (Dev, QA, Prod) & machineID. – Developer63 Feb 06 '20 at 10:50
  • Agree with @Developer63, very good answer to a pesky problem I was having. – Carlos Mar 13 '20 at 17:04
1

I don't know if you can do this with native layouts, but it's relatively easy to implement your own LayoutRenderer to achieve this.

I've done something pretty close to what you want a few time ago :

    [LayoutRenderer("UniqueName")]
    public class UniqueNameLayoutRenderer : LayoutRenderer
    {
        #region Fields (1)

        private string _constantName;

        #endregion Fields

        #region Enums (1)

        public enum PatternType
        {
            /// <summary>
            /// Long date + current process ID
            /// </summary>
            LongDateAndPID,
            /// <summary>
            /// Long date (including ms)
            /// </summary>
            LongDate,
        }

        #endregion Enums

        #region Properties (2)

        public string ConstantName
        {
            get
            {
                if (_constantName == null)
                {
                    if (Format == PatternType.LongDateAndPID)
                    {
                        string pid;
                        try
                        {
                            pid = Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture);
                        }
                        catch
                        {
                            pid = "000"; 
                        }

                        _constantName = DateTime.Now.ToString("yyyy-MM-dd_HHmmss-ffff") + "_pid" + pid;
                    }
                    else if (Format == PatternType.LongDate)
                    {
                        _constantName = DateTime.Now.ToString("yyyy-MM-dd_HHmmss-ffff");
                    }
                }

                return _constantName;
            }
        }

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

        #endregion Properties

        #region Methods (2)

        // Protected Methods (1) 

        protected override void Append(StringBuilder builder, LogEventInfo logEvent)
        {
            builder.Append(ConstantName);
        }
        // Private Methods (1) 

        #endregion Methods
    }
}

It will generate the name of the file at the first call with current long time, and keep it until the end of execution. I have mostly used it with NLog 2 but it looks like to work well with NLog 3.2.

It's intended to be used with

"{UniqueName:format=LongDate}" 

in NLog configuration file for example. "LongDateAndPID" adds process id at the end of name, it can be useful to track different instances with rolling logs enabled.

Before to use it, you should register the appender with :

<extensions><add assembly="MyAssemblyWhichContainsRenderer"/></extensions>

To use it with rolling enabled, just use something like this :

<target xsi:type="File"
        name="logfile"
        fileName="${logDirectory}\logs-${UniqueName:format=LongDateAndPID}.txt"
        layout="${longdate} | ${level:uppercase=true} (${threadid}) | ${callsite} : ${message} ${exception:format=ToString}"
        archiveFileName="${logDirectory}\logs-${UniqueName:format=LongDateAndPID}_{##}.txt"
        archiveAboveSize="25165824"
        concurrentWrites="false"
        archiveNumbering="Sequence"
        maxArchiveFiles="99999" />

(of course this is just an example)

Feel free to adapt the layout renderer to you own needs.

Julian
  • 33,915
  • 22
  • 119
  • 174
AFract
  • 8,868
  • 6
  • 48
  • 70
  • I've been moved temporarily to a different project. When I get back to this project in a few days, I'll try this out and award the bounty as necessary. – Brandon Dec 08 '14 at 16:55
  • I can't seem to get NLog to recognize the renderer. I'm creating my logging configuration in code (no config file) and when I apply my Layout string to `FileName` of my `FileTarget`, I get an ArgumentException with `LayoutRenderer cannot be found: 'CreationDate'` (I renamed the renderer, obviously) – Brandon Dec 10 '14 at 15:04
  • I figured out how to get NLog to recognize my LayoutRenderer (`ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition()`) and it's now working, however, this LayoutRenderer does the same thing as just caching the file name. The file name never changes when it's archived. – Brandon Dec 10 '14 at 15:47
  • Hello. To register your custom(s) renderers you can also add in NLog configuration file. I don't understand your point about archives names. My example with "archiveFileName" doesn't help ? Have you tried the "archiveNumbering=Date" attribute for example ? (see NLog documentation) – AFract Dec 10 '14 at 17:08
  • I'm not using a configuration file; Everything I'm doing is being done from code. I didn't see the `archiveFileName` in your answer. I did try it and it worked once, and then did the same thing in that it cached the file name again. I'll update my question with what I'm looking to have happen as I'm guessing it isn't clear – Brandon Dec 10 '14 at 17:42
  • This specific attribute wasn't in my answer because it was using {##} to get an increment log number rather than a date. – AFract Dec 10 '14 at 17:45
  • Read. What it does with my rule for layoutrenderer, plus a valid date pattern for ArchiveDateFormat ? Isn't it ok ? – AFract Dec 10 '14 at 18:47
  • No, your layout renderer caches the file name, and `ArchiveDateFormat` adds ".DateFormat" to the end of the file name. I may have hacked together a solution that does an acceptable 60% of what I want. – Brandon Dec 10 '14 at 18:56
0

The problem is that your are using a mask that include HOURS, MINUTES and SECONDS in the file name. I have the same issue in the pass. Probably you need to think about what is the real benefit for having HOURS, MINUTES and SECONDS in the Log file name IF you have a Daily Rollover. To setup correctly use a format that match with your archive criteria. EX: ArchiveEvery=Day yyyyMMdd.log

ArchiveEvery=Hour yyyyMMdd hh.log

ArchiveEvery=Minute yyyyMMdd hh.mm.log

In the other hand: do you really need the mix of 2 behaviors for archiving?

If the answer is YES, go ahead and implement you own appender as the other post say. Else try rolling files every hour, this will keep your files smallers.

Hope this help

Miguel
  • 3,786
  • 2
  • 19
  • 32
  • 1
    It's a requirement from the customer that their logs be in the format of "yyyy-MM-dd hh.mm.ss"; They will not change their mind on this. We do need the 2 behaviors because of the data being logged. – Brandon Dec 12 '14 at 14:57