* some trivial changes in visibility, regions etc.
* additional functions, properties, members that aren't yet used from existing 
code.
* NextCheckDate -> GetRollDateTimeRelative:
  Function works identically if called with relativePeriod==1. Existing code 
does this.
  Additionally any count of positive or negative period jumps can be calculated.
* Fully isolated getting last write time in a function GetLastWriteTime
* Split CombinePath into a static version and a member function to call it. 
Needed
  to allow implementing and testing one of the new functions as static function.
  That new, static function uses CombinePath so i needed a static version.

diff -r 76c5f9136b8f -r 569e06c6cfb3 src/Appender/RollingFileAppender.cs
--- a/src/Appender/RollingFileAppender.cs       Tue Jan 22 14:31:24 2013 +0100
+++ b/src/Appender/RollingFileAppender.cs       Tue Jan 22 14:31:43 2013 +0100
@@ -169,10 +169,6 @@
                        Composite       = 3
                }
 
-               #endregion
-
-               #region Protected Enums
-
                /// <summary>
                /// The code assumes that the following 'time' constants are in 
a increasing sequence.
                /// </summary>
@@ -181,7 +177,7 @@
                /// The code assumes that the following 'time' constants are in 
a increasing sequence.
                /// </para>
                /// </remarks>
-               protected enum RollPoint
+               public enum RollPoint
                {
                        /// <summary>
                        /// Roll the log not based on the date
@@ -219,7 +215,7 @@
                        TopOfMonth                      = 5
                }
 
-               #endregion Protected Enums
+               #endregion Public Enums
 
                #region Public Instance Constructors
 
@@ -339,6 +335,35 @@
                }
 
                /// <summary>
+               /// Gets or sets the maximum number of periods to keep backups 
for.
+               /// </summary>
+               /// <value>
+               /// The maximum number of periods to keep backups for.
+               /// </value>
+               /// <remarks>
+               /// <para>
+               /// The value refers to the count of periods backups shall be 
kept for. Any 
+               /// backups that are older than MaxDateRollBackups * Period get 
deleted,
+               /// regardless whether or how many backups exist younger than 
this exist. 
+               /// </para>
+               /// <para>
+               /// Example: If set to 10 and roll pattern is daily everything 
older than 10 
+               /// days gets deleted.  
+               /// </para>
+               /// <para>
+               /// If set to zero, then there will be no time roll backup 
files.
+               /// </para>
+               /// <para>
+               /// If a negative number is supplied then no deletions will be 
made.
+               /// </para>
+               /// </remarks>
+               public int MaxDateRollBackups
+               {
+                       get { return m_maxDateRollBackups; }
+                       set { m_maxDateRollBackups = value; }
+               }
+
+               /// <summary>
                /// Gets or sets the maximum size that the output file is 
allowed to reach
                /// before being rolled over to backup files.
                /// </summary>
@@ -598,7 +623,7 @@
                                if (n >= m_nextCheck)
                                {
                                        m_now = n;
-                                       m_nextCheck = NextCheckDate(m_now, 
m_rollPoint);
+                                       m_nextCheck = 
GetRollDateTimeRelative(m_now, m_rollPoint, 1);
 
                                        RollOverTime(true);
                                }
@@ -797,18 +822,7 @@
                                        DateTime last;
                                        using(SecurityContext.Impersonate(this))
                                        {
-#if !NET_1_0 && !CLI_1_0 && !NETCF
-                                               if (DateTimeStrategy is 
UniversalDateTime)
-                                               {
-                                                       last = 
System.IO.File.GetLastWriteTimeUtc(m_baseFileName);
-                                               }
-                                               else
-                                               {
-#endif
-                                                       last = 
System.IO.File.GetLastWriteTime(m_baseFileName);
-#if !NET_1_0 && !CLI_1_0 && !NETCF
-                                               }
-#endif
+                                               last = 
GetLastWriteTime(m_baseFileName);
                                        }
                                        LogLog.Debug(declaringType, 
"["+last.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"]
 vs. 
["+m_now.ToString(m_datePattern,System.Globalization.DateTimeFormatInfo.InvariantInfo)+"]");
 
@@ -824,6 +838,29 @@
                }
 
                /// <summary>
+               /// Gets the last write time for the file given
+               /// </summary>
+               /// <param name="fileName">the path to the file</param>
+               /// <returns>the time of last write</returns>
+               private DateTime GetLastWriteTime(string fileName)
+               {
+                       DateTime last;
+#if !NET_1_0 && !CLI_1_0 && !NETCF
+                       if (DateTimeStrategy is UniversalDateTime)
+                       {
+                               last = 
System.IO.File.GetLastWriteTimeUtc(fileName);
+                       }
+                       else
+                       {
+#endif
+                               last = 
System.IO.File.GetLastWriteTime(fileName);
+#if !NET_1_0 && !CLI_1_0 && !NETCF
+                       }
+#endif
+                       return last;
+               }
+
+               /// <summary>
                /// Initializes based on existing conditions at time of <see 
cref="ActivateOptions"/>.
                /// </summary>
                /// <remarks>
@@ -1036,7 +1073,7 @@
                        for(int i = (int)RollPoint.TopOfMinute; i <= 
(int)RollPoint.TopOfMonth; i++)
                        {
                                // Get string representation of next pattern
-                               string r1 = NextCheckDate(s_date1970, 
(RollPoint)i).ToString(datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
+                               string r1 = GetRollDateTimeRelative(s_date1970, 
(RollPoint)i, 1).ToString(datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo);
 
                                LogLog.Debug(declaringType, "Type = ["+i+"], r0 
= ["+r0+"], r1 = ["+r1+"]");
 
@@ -1090,7 +1127,7 @@
                                }
 
                                // next line added as this removes the name 
check in rollOver
-                               m_nextCheck = NextCheckDate(m_now, m_rollPoint);
+                               m_nextCheck = GetRollDateTimeRelative(m_now, 
m_rollPoint, 1);
                        }
                        else
                        {
@@ -1131,15 +1168,29 @@
                #region Roll File
 
                /// <summary>
-               ///
+               /// Convenience member function used to call its static 
equivalent.
                /// </summary>
-               /// <param name="path1"></param>
-               /// <param name="path2">.1, .2, .3, etc.</param>
-               /// <returns></returns>
-               private string CombinePath(string path1, string path2)
+               /// <param name="path1">unindexed raw log file path</param>
+               /// <param name="path2">date and/or size roll index. .1, 
.20121224, .20121224.2 or something like that</param>
+               /// <returns>the combined path</returns>
+               protected string CombinePath(string path1, string path2)
+               {
+                       return CombinePath(path1, path2, 
m_preserveLogFileNameExtension);
+               }
+
+               /// <summary>
+               /// Combines a path from the given elements such that if 
preserveLogFileNameExtension is
+               /// set to true the date and/or size roll index given in path2 
gets inserted or appended correctly
+               /// into/behind the unindexed path given in path1
+               /// </summary>
+               /// <param name="path1">unindexed raw log file path</param>
+               /// <param name="path2">date and/or size roll index. .1, 
.20121224, .20121224.2 or something like that</param>
+               /// <param name="preserveLogFileNameExtension">preserve the log 
file extension (insert before) or not (append)</param>
+               /// <returns>the combined path</returns>
+               protected static string CombinePath(string path1, string path2, 
bool preserveLogFileNameExtension)
                {
                        string extension = Path.GetExtension(path1);
-                       if (m_preserveLogFileNameExtension && extension.Length 
> 0)
+                       if (preserveLogFileNameExtension && extension.Length > 
0)
                        {
                                return 
Path.Combine(Path.GetDirectoryName(path1), 
Path.GetFileNameWithoutExtension(path1) + path2 + extension);
                        }
@@ -1212,6 +1263,83 @@
                }
 
                /// <summary>
+               /// Deletes all files that are outdated considering 
m_maxDateRollBackups. 
+               /// </summary>
+               protected void DeleteOutdatedFiles()
+               {
+                       if (m_maxDateRollBackups >= 0)
+                       {
+                               // find all matching files. 
+                               // we don't care about roll indexes or roll 
date patterns that cannot have been created by us.
+                               // we just handle each file that has the 
matching base name and the matching extension.
+                               string[] fileNames = 
Directory.GetFiles(Path.GetDirectoryName(File), 
Path.GetFileNameWithoutExtension(CombinePath(m_baseFileName, ".*", 
m_preserveLogFileNameExtension)));
+                               ArrayList fileDeleteList = 
GetFileDeleteList(fileNames, DateTimeStrategy.Now, m_baseFileName, 
m_datePattern, m_rollPoint,
+                                       m_maxDateRollBackups, 
m_preserveLogFileNameExtension);
+                               foreach (string fileName in fileDeleteList)
+                               {
+                                       DeleteFile(fileName);
+                               }
+                       }
+
+               }
+
+               /// <summary>
+               /// Gets a list of files to delete considering the rollPoint, 
maxDateRollBackups, 
+               /// the given current time now, the baseFile name and the 
datePattern given by 
+               /// filtering the file name list given with a generated 
positive (keep) list.
+               /// Files in fileNames that don't appear in the internal keep 
pattern list get listed in the 
+               /// file list returned and can be in turn deleted by the caller.
+               /// </summary>
+               /// <param name="fileNames">list of file names to check for 
deletion</param>
+               /// <param name="now">the point of time to be used for deciding 
whether files are outdated or not</param>
+               /// <param name="baseFile">the base file name of all log 
files</param>
+               /// <param name="datePattern">the date pattern to be 
used</param>
+               /// <param name="rollPoint">the roll point to be used</param>
+               /// <param name="maxDateRollBackups">the maximum number of date 
roll backups</param>
+               /// <param name="preserveLogFileNameExtension">preserve log 
file extensions (insert date/size index) or not (append date/size index)</param>
+               /// <returns>list of files to be deleted as a filtering result 
on fileNames at input</returns>
+               protected static ArrayList GetFileDeleteList(string[] 
fileNames, DateTime now, string baseFile, string datePattern, RollPoint 
rollPoint, int maxDateRollBackups, bool preserveLogFileNameExtension)
+               {
+                       ArrayList list = new ArrayList();
+                       if (maxDateRollBackups >= 0)
+                       {
+                               // remark: we cannot use file write time here 
because
+                               //               * file might have been touched 
somehow after period
+                               //               * file gets written to after 
period if a footer is configured and thus been added when file is closed
+                               // therefore we now create a positive pattern 
list, match each file name found against it and if
+                               // it doesn't match delete the file
+
+                               string[] positiveList = new 
string[maxDateRollBackups + 1];
+                               for (int i = 0; i <= maxDateRollBackups; i++)
+                               {
+                                       DateTime periodStart = 
GetRollDateTimeRelative(now, rollPoint, -i);
+                                       string periodPattern = 
periodStart.ToString(datePattern, 
System.Globalization.DateTimeFormatInfo.InvariantInfo) + "*";
+                                       string periodPatternPath = 
CombinePath(baseFile, periodPattern, preserveLogFileNameExtension);
+                                       positiveList[i] = 
Path.GetFileName(periodPatternPath).Split(new Char[] { '*' })[0];
+                               }
+
+                               foreach (string fileName in fileNames)
+                               {
+                                       string fn = Path.GetFileName(fileName);
+                                       bool keep = false;
+                                       foreach (string fileStart in 
positiveList)
+                                       {
+                                               if (fn.StartsWith(fileStart))
+                                               {
+                                                       keep = true;
+                                                       break;
+                                               }
+                                       }
+                                       if (!keep)
+                                       {
+                                               list.Add(fileName);
+                                       }
+                               }
+                       }
+                       return list;
+               }
+
+               /// <summary>
                /// Renames file <paramref name="fromFile"/> to file <paramref 
name="toFile"/>.
                /// </summary>
                /// <param name="fromFile">Name of existing file to 
roll.</param>
@@ -1466,90 +1594,94 @@
                        }
                }
 
-               #endregion
-
-               #region NextCheckDate
-
                /// <summary>
-               /// Get the start time of the next window for the current 
rollpoint
+               /// Get the beginning of the relativePeriod-th following or 
preceeding period relative to the 
+               /// the period that contains the given dateTime.
                /// </summary>
-               /// <param name="currentDateTime">the current date</param>
+               /// <param name="dateTime">the date</param>
                /// <param name="rollPoint">the type of roll point we are 
working with</param>
-               /// <returns>the start time for the next roll point an interval 
after the currentDateTime date</returns>
+               /// <param name="relativePeriod">
+               /// the number of the period relative to the dateTime given. 
+               /// 0 means the beginning of the period that contains the given 
dateTime.
+               /// 1 means the beginning of the period following the period 
that contains the given dateTime.
+               /// -7 means the beginning of the 7th period before the period 
that contains the given dateTime.
+               /// </param>
+               /// <returns>the beginning of the period requested.</returns>
                /// <remarks>
                /// <para>
-               /// Returns the date of the next roll point after the 
currentDateTime date passed to the method.
+               /// Get the beginning of the relativePeriod-th following or 
preceeding period relative to the 
+               /// the period that contains the given dateTime.
                /// </para>
                /// <para>
                /// The basic strategy is to subtract the time parts that are 
less significant
                /// than the rollpoint from the current time. This should roll 
the time back to
-               /// the start of the time window for the current rollpoint. 
Then we add 1 window
-               /// worth of time and get the start time of the next window for 
the rollpoint.
+               /// the start of the time window for the current rollpoint. 
Then we add relativePeriod periods
+               /// worth of time and get the beginning of the period requested.
                /// </para>
                /// </remarks>
-               protected DateTime NextCheckDate(DateTime currentDateTime, 
RollPoint rollPoint)
+               protected static DateTime GetRollDateTimeRelative(DateTime 
dateTime, RollPoint rollPoint, int relativePeriod)
                {
                        // Local variable to work on (this does not look very 
efficient)
-                       DateTime current = currentDateTime;
+                       DateTime result = dateTime;
 
                        // Do slightly different things depending on what the 
type of roll point we want.
                        switch(rollPoint)
                        {
                                case RollPoint.TopOfMinute:
-                                       current = 
current.AddMilliseconds(-current.Millisecond);
-                                       current = 
current.AddSeconds(-current.Second);
-                                       current = current.AddMinutes(1);
+                                       result = 
result.AddMilliseconds(-result.Millisecond);
+                                       result = 
result.AddSeconds(-result.Second);
+                                       result = 
result.AddMinutes(relativePeriod);
                                        break;
 
                                case RollPoint.TopOfHour:
-                                       current = 
current.AddMilliseconds(-current.Millisecond);
-                                       current = 
current.AddSeconds(-current.Second);
-                                       current = 
current.AddMinutes(-current.Minute);
-                                       current = current.AddHours(1);
+                                       result = 
result.AddMilliseconds(-result.Millisecond);
+                                       result = 
result.AddSeconds(-result.Second);
+                                       result = 
result.AddMinutes(-result.Minute);
+                                       result = 
result.AddHours(relativePeriod);
                                        break;
 
                                case RollPoint.HalfDay:
-                                       current = 
current.AddMilliseconds(-current.Millisecond);
-                                       current = 
current.AddSeconds(-current.Second);
-                                       current = 
current.AddMinutes(-current.Minute);
+                                       result = 
result.AddMilliseconds(-result.Millisecond);
+                                       result = 
result.AddSeconds(-result.Second);
+                                       result = 
result.AddMinutes(-result.Minute);
 
-                                       if (current.Hour < 12)
+                                       if (result.Hour < 12)
                                        {
-                                               current = current.AddHours(12 - 
current.Hour);
+                                               result = 
result.AddHours(-result.Hour);
                                        }
                                        else
                                        {
-                                               current = 
current.AddHours(-current.Hour);
-                                               current = current.AddDays(1);
+                                               result = 
result.AddHours(12-result.Hour);
                                        }
+                                       result = result.AddHours(12 * 
relativePeriod);
                                        break;
 
                                case RollPoint.TopOfDay:
-                                       current = 
current.AddMilliseconds(-current.Millisecond);
-                                       current = 
current.AddSeconds(-current.Second);
-                                       current = 
current.AddMinutes(-current.Minute);
-                                       current = 
current.AddHours(-current.Hour);
-                                       current = current.AddDays(1);
+                                       result = 
result.AddMilliseconds(-result.Millisecond);
+                                       result = 
result.AddSeconds(-result.Second);
+                                       result = 
result.AddMinutes(-result.Minute);
+                                       result = result.AddHours(-result.Hour);
+                                       result = result.AddDays(relativePeriod);
                                        break;
 
                                case RollPoint.TopOfWeek:
-                                       current = 
current.AddMilliseconds(-current.Millisecond);
-                                       current = 
current.AddSeconds(-current.Second);
-                                       current = 
current.AddMinutes(-current.Minute);
-                                       current = 
current.AddHours(-current.Hour);
-                                       current = current.AddDays(7 - 
(int)current.DayOfWeek);
+                                       result = 
result.AddMilliseconds(-result.Millisecond);
+                                       result = 
result.AddSeconds(-result.Second);
+                                       result = 
result.AddMinutes(-result.Minute);
+                                       result = result.AddHours(-result.Hour);
+                                       result = 
result.AddDays(7*relativePeriod - (int)result.DayOfWeek);
                                        break;
 
                                case RollPoint.TopOfMonth:
-                                       current = 
current.AddMilliseconds(-current.Millisecond);
-                                       current = 
current.AddSeconds(-current.Second);
-                                       current = 
current.AddMinutes(-current.Minute);
-                                       current = 
current.AddHours(-current.Hour);
-                                       current = current.AddDays(1 - 
current.Day); /* first day of month is 1 not 0 */
-                                       current = current.AddMonths(1);
+                                       result = 
result.AddMilliseconds(-result.Millisecond);
+                                       result = 
result.AddSeconds(-result.Second);
+                                       result = 
result.AddMinutes(-result.Minute);
+                                       result = result.AddHours(-result.Hour);
+                                       result = result.AddDays(1 - 
result.Day); /* first day of month is 1 not 0 */
+                                       result = 
result.AddMonths(relativePeriod);
                                        break;
                        }
-                       return current;
+                       return result;
                }
 
                #endregion
@@ -1612,6 +1744,11 @@
                private int m_countDirection = -1;
 
                /// <summary>
+               /// There is zero backup files by default
+               /// </summary>
+               private int m_maxDateRollBackups = 0;
+
+               /// <summary>
                /// The rolling mode used in this appender.
                /// </summary>
                private RollingMode m_rollingStyle = RollingMode.Composite;

Reply via email to