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