Index: src/Appender/RollingFileAppender.cs =================================================================== --- src/Appender/RollingFileAppender.cs (revision 883140) +++ src/Appender/RollingFileAppender.cs (working copy) @@ -318,8 +318,34 @@ get { return m_maxSizeRollBackups; } set { m_maxSizeRollBackups = value; } } - + /// + /// Gets or sets the maximum number of backup files that are kept before + /// the oldest is erased. + /// + /// + /// The maximum number of backup files that are kept before the oldest is + /// erased. + /// + /// + /// + /// If set to zero, then there will be no backup files and the log file + /// will be truncated when it reaches . + /// + /// + /// If a negative number is supplied then no deletions will be made. Note + /// that this could result in very slow performance as a large number of + /// files are rolled over unless is used. + /// + /// Added by Joshua Masek of RoviSys - see http://issues.apache.org/jira/browse/LOG4NET-27 + /// + public int MaxDateRollBackups + { + get { return m_maxDateRollBackups; } + set { m_maxDateRollBackups = value; } + } + + /// /// Gets or sets the maximum size that the output file is allowed to reach /// before being rolled over to backup files. /// @@ -1167,7 +1193,93 @@ RollFile(File, m_scheduledFilename); } - + + //delete old backups + if (m_rollDate && m_maxDateRollBackups != 0) + { + FileInfo fi = new FileInfo(m_baseFileName); + DateTime currentDate = m_now; + string dirName = fi.DirectoryName + Path.DirectorySeparatorChar; + int tempMaxDateRollBackups = m_maxDateRollBackups; + if (tempMaxDateRollBackups < 0) tempMaxDateRollBackups = 0; + int tempMaxSizeRollBackups = m_maxSizeRollBackups; + if (tempMaxSizeRollBackups < 0) tempMaxSizeRollBackups = 0; + + // Build all valid file names + string[] validFilesName = new string[(tempMaxDateRollBackups + 1) * (tempMaxSizeRollBackups + 1)]; + for (int iDate = 0; iDate <= tempMaxDateRollBackups; iDate++) + { + string loopFileNameBase = null; + if ((iDate == 0) && m_staticLogFileName) + { + loopFileNameBase = fi.Name; + } + else + { + if (iDate > 0) + { + currentDate = PreviousCheckDate(currentDate, m_rollPoint); + } + string loopDateFormat = currentDate.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo); + loopFileNameBase = fi.Name + loopDateFormat; + } + + for (int iSize = 0; iSize <= tempMaxSizeRollBackups; iSize++) + { + int iFileName = (iDate * (tempMaxSizeRollBackups + 1) + iSize); + if (iSize == 0) + { + validFilesName[iFileName] = loopFileNameBase; + } + else + { + validFilesName[iFileName] = loopFileNameBase + "." + iSize; + } + } + } + + // Check if existing files are in valid file names list, also delete it + foreach (string current in GetExistingFiles(m_baseFileName)) + { + bool valid = false; + foreach (string validFileName in validFilesName) + { + if (validFileName.Equals(current)) + { + valid = true; + break; + } + } + if (valid == false) + { + string filenameToDelete = dirName + current; + DateTime fileLastModified = System.IO.File.GetLastWriteTime(filenameToDelete); + DateTime previousCheckDateAfterFileModified = PreviousCheckDate(fileLastModified, m_rollPoint); + string formattedCurrent = fileLastModified.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo); + string formattedNext = previousCheckDateAfterFileModified.ToString(m_datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo); + int lastCharIndexToUse = current.Length - 1; + for (int charLoop = current.Length - 1; charLoop >= 0; charLoop--) + { + byte dummyByte = 0; + if ((charLoop < (current.Length - 1)) && (current.Substring(charLoop, 1) == ".")) + { + lastCharIndexToUse = charLoop - 1; + break; + } + else if (!byte.TryParse(current.Substring(charLoop, 1), out dummyByte)) + { + break; + } + } + string currentBase = current.Substring(0, lastCharIndexToUse + 1); + if (currentBase.Equals(fi.Name) || currentBase.Equals(fi.Name + formattedCurrent) || currentBase.Equals(fi.Name + formattedNext)) + { + DeleteFile(filenameToDelete); + } + } + } + } + //We've cleared out the old date and are ready for the new m_curSizeRollBackups = 0; @@ -1180,8 +1292,85 @@ SafeOpenFile(m_baseFileName, false); } } - + /// + /// Roll on to the previous interval before the date passed + /// + /// the current date + /// the type of roll point we are working with + /// the previous roll point an interval before the currentDateTime date + /// + /// Advances the date to the previous roll point before the + /// currentDateTime date passed to the method. + /// + protected DateTime PreviousCheckDate(DateTime currentDateTime, RollPoint rollPoint) + { + // Local variable to work on (this does not look very efficient) + DateTime current = currentDateTime; + + // Do different things depending on what the type of roll point we are going for is + switch (rollPoint) + { + case RollPoint.TopOfMinute: + current = current.AddMilliseconds(-current.Millisecond); + current = current.AddSeconds(-current.Second); + current = current.AddMinutes(-1); + break; + + case RollPoint.TopOfHour: + current = current.AddMilliseconds(-current.Millisecond); + current = current.AddSeconds(-current.Second); + current = current.AddMinutes(-current.Minute); + current = current.AddHours(-1); + break; + + case RollPoint.HalfDay: + current = current.AddMilliseconds(-current.Millisecond); + current = current.AddSeconds(-current.Second); + current = current.AddMinutes(-current.Minute); + + if (current.Hour < 12) + { + current = current.AddHours(-current.Hour - 12); + } + else + { + current = current.AddHours(-current.Hour); + } + 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); + 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(-(int)current.DayOfWeek - 7); + 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(-current.Day); + // To be in previous month + current = current.AddDays(-1); + current = current.AddDays(-DateTime.DaysInMonth(current.Year, current.Month) + 1); + + break; + } + return current; + } + + /// /// Renames file to file . /// /// Name of existing file to roll. @@ -1572,6 +1761,12 @@ private int m_maxSizeRollBackups = 0; /// + /// There is zero backup files by default + /// + /// Added by Joshua Masek of RoviSys - see http://issues.apache.org/jira/browse/LOG4NET-27 + private int m_maxDateRollBackups = 0; + + /// /// How many sized based backups have been made so far /// private int m_curSizeRollBackups = 0;