Author: ceki Date: Tue Oct 7 15:23:33 2008 New Revision: 1820 Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedCleaner.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithCleanTest.java Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java logback/trunk/logback-site/src/site/pages/manual/appenders.html Log: Fixing LBCORE-11 - Allow TimeBasedRollingPolicy to delete old files if asked by the user. - PeriodicityType are now listed in an enum. Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java Tue Oct 7 15:23:33 2008 @@ -13,6 +13,8 @@ import java.util.Date; import java.util.concurrent.Future; +import sun.misc.Cleaner; + import ch.qos.logback.core.rolling.helper.AsynchronousCompressor; import ch.qos.logback.core.rolling.helper.Compressor; import ch.qos.logback.core.rolling.helper.CompressionMode; @@ -20,6 +22,7 @@ import ch.qos.logback.core.rolling.helper.FileNamePattern; import ch.qos.logback.core.rolling.helper.RenameUtil; import ch.qos.logback.core.rolling.helper.RollingCalendar; +import ch.qos.logback.core.rolling.helper.TimeBasedCleaner; /** * <code>TimeBasedRollingPolicy</code> is both easy to configure and quite @@ -48,6 +51,8 @@ String lastGeneratedFileName; Future<?> future; + TimeBasedCleaner tbCleaner; + public void setCurrentTime(long timeInMillis) { currentTime = timeInMillis; isTimeForced = true; @@ -109,8 +114,9 @@ // currentTime = System.currentTimeMillis(); lastCheck.setTime(getCurrentTime()); - nextCheck = rc.getNextCheckMillis(lastCheck); + nextCheck = rc.getNextTriggeringMillis(lastCheck); + tbCleaner = new TimeBasedCleaner(fileNamePattern, rc, 5); // Date nc = new Date(); // nc.setTime(nextCheck); } @@ -130,6 +136,8 @@ } } + tbCleaner.clean(new Date(getCurrentTime())); + // let's update the parent active file name setParentFileName(getNewActiveFileName()); @@ -172,13 +180,13 @@ * file equals the file name for the current period as computed by the * <b>FileNamePattern</b> option. * - * The RollingPolicy must know wether it is responsible for changing the name + * <p>The RollingPolicy must know wether it is responsible for changing the name * of the active file or not. If the active file name is set by the user via * the configuration file, then the RollingPolicy must let it like it is. If * the user does not specify an active file name, then the RollingPolicy * generates one. * - * To be sure that the file name used by the parent class has been generated + * <p>To be sure that the file name used by the parent class has been generated * by the RollingPolicy and not specified by the user, we keep track of the * last generated name object and compare its reference to the parent file * name. If they match, then the RollingPolicy knows it's responsible for the @@ -210,7 +218,7 @@ // addInfo("elapsedPeriodsFileName set to "+elapsedPeriodsFileName); lastCheck.setTime(time); - nextCheck = rc.getNextCheckMillis(lastCheck); + nextCheck = rc.getNextTriggeringMillis(lastCheck); Date x = new Date(); x.setTime(nextCheck); Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java Tue Oct 7 15:23:33 2008 @@ -0,0 +1,16 @@ +package ch.qos.logback.core.rolling.helper; + +public enum PeriodicityType { + + ERRONEOUS, TOP_OF_SECOND, TOP_OF_MINUTE, TOP_OF_HOUR, HALF_DAY, TOP_OF_DAY, TOP_OF_WEEK, TOP_OF_MONTH; + + // The followed list is assued to be in + static PeriodicityType[] VALID_ORDERED_LIST = new PeriodicityType[] { + PeriodicityType.TOP_OF_SECOND, + PeriodicityType.TOP_OF_MINUTE, + PeriodicityType.TOP_OF_HOUR, + PeriodicityType.TOP_OF_DAY, + PeriodicityType.TOP_OF_WEEK, + PeriodicityType.TOP_OF_MONTH }; + +} Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java Tue Oct 7 15:23:33 2008 @@ -19,36 +19,23 @@ import ch.qos.logback.core.spi.ContextAwareBase; - - /** - * RollingCalendar is a helper class to - * {@link ch.qos.logback.core.rolling.TimeBasedRollingPolicy } - * or similar timed-based rolling policies. Given a periodicity type and the - * current time, it computes the start of the next interval. - * + * RollingCalendar is a helper class to + * {@link ch.qos.logback.core.rolling.TimeBasedRollingPolicy } or similar + * timed-based rolling policies. Given a periodicity type and the current time, + * it computes the start of the next interval (i.e. the triggering date). + * * @author Ceki Gülcü - * - * */ + * + */ public class RollingCalendar extends GregorianCalendar { private static final long serialVersionUID = -5937537740925066161L; - // The gmtTimeZone is used only in computeCheckPeriod() method. static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT"); - // The code assumes that the following constants are in a increasing - // sequence. - public static final int TOP_OF_TROUBLE = -1; - public static final int TOP_OF_SECOND = 0; - public static final int TOP_OF_MINUTE = 1; - public static final int TOP_OF_HOUR = 2; - public static final int HALF_DAY = 3; - public static final int TOP_OF_DAY = 4; - public static final int TOP_OF_WEEK = 5; - public static final int TOP_OF_MONTH = 6; - int type = TOP_OF_TROUBLE; + PeriodicityType type = PeriodicityType.ERRONEOUS; public RollingCalendar() { super(); @@ -59,15 +46,15 @@ } public void init(String datePattern) { - type = computeTriggeringPeriod(datePattern); + type = computePeriodicity(datePattern); } - private void setType(int type) { + private void setType(PeriodicityType type) { this.type = type; } - public long getNextCheckMillis(Date now) { - return getNextCheckDate(now).getTime(); + public long getNextTriggeringMillis(Date now) { + return getNextTriggeringDate(now).getTime(); } // This method computes the roll over period by looping over the @@ -78,32 +65,32 @@ // formatting is done in GMT and not local format because the test // logic is based on comparisons relative to 1970-01-01 00:00:00 // GMT (the epoch). - public int computeTriggeringPeriod(String datePattern) { - RollingCalendar rollingCalendar = - new RollingCalendar(GMT_TIMEZONE, Locale.getDefault()); + public PeriodicityType computePeriodicity(String datePattern) { + RollingCalendar rollingCalendar = new RollingCalendar(GMT_TIMEZONE, Locale + .getDefault()); // set sate to 1970-01-01 00:00:00 GMT Date epoch = new Date(0); if (datePattern != null) { - for (int i = TOP_OF_SECOND; i <= TOP_OF_MONTH; i++) { + for (PeriodicityType i : PeriodicityType.VALID_ORDERED_LIST) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); - simpleDateFormat.setTimeZone(GMT_TIMEZONE); // do all date formatting in GMT + simpleDateFormat.setTimeZone(GMT_TIMEZONE); // all date formatting done in GMT String r0 = simpleDateFormat.format(epoch); rollingCalendar.setType(i); - Date next = new Date(rollingCalendar.getNextCheckMillis(epoch)); + Date next = new Date(rollingCalendar.getNextTriggeringMillis(epoch)); String r1 = simpleDateFormat.format(next); - //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1); + // System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1); if ((r0 != null) && (r1 != null) && !r0.equals(r1)) { return i; } } } - - return TOP_OF_TROUBLE; // Deliberately head for trouble... + // we failed + return PeriodicityType.ERRONEOUS; } public void printPeriodicity(ContextAwareBase cab) { @@ -148,20 +135,20 @@ } } - public Date getNextCheckDate(Date now) { + public Date getRelativeDate(Date now, int periods) { this.setTime(now); switch (type) { case TOP_OF_SECOND: this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.SECOND, 1); + this.add(Calendar.SECOND, periods); break; case TOP_OF_MINUTE: this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.MINUTE, 1); + this.add(Calendar.MINUTE, periods); break; @@ -169,23 +156,7 @@ this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.HOUR_OF_DAY, 1); - - break; - - case HALF_DAY: - this.set(Calendar.MINUTE, 0); - this.set(Calendar.SECOND, 0); - this.set(Calendar.MILLISECOND, 0); - - int hour = get(Calendar.HOUR_OF_DAY); - - if (hour < 12) { - this.set(Calendar.HOUR_OF_DAY, 12); - } else { - this.set(Calendar.HOUR_OF_DAY, 0); - this.add(Calendar.DAY_OF_MONTH, 1); - } + this.add(Calendar.HOUR_OF_DAY, periods); break; @@ -194,7 +165,7 @@ this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.DATE, 1); + this.add(Calendar.DATE, periods); break; @@ -204,7 +175,7 @@ this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.WEEK_OF_YEAR, 1); + this.add(Calendar.WEEK_OF_YEAR, periods); break; @@ -214,7 +185,7 @@ this.set(Calendar.MINUTE, 0); this.set(Calendar.SECOND, 0); this.set(Calendar.MILLISECOND, 0); - this.add(Calendar.MONTH, 1); + this.add(Calendar.MONTH, periods); break; @@ -224,4 +195,8 @@ return getTime(); } + + public Date getNextTriggeringDate(Date now) { + return getRelativeDate(now, 1); + } } Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedCleaner.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedCleaner.java Tue Oct 7 15:23:33 2008 @@ -0,0 +1,32 @@ +package ch.qos.logback.core.rolling.helper; + +import java.io.File; +import java.util.Date; + +public class TimeBasedCleaner { + + FileNamePattern fileNamePattern; + RollingCalendar rc; + int numberOfPeriods; + + public TimeBasedCleaner(FileNamePattern fileNamePattern, RollingCalendar rc, + int numberOfPeriods) { + this.fileNamePattern = fileNamePattern; + this.rc = rc; + // + this.numberOfPeriods = -numberOfPeriods -1; + } + + public void clean(Date now) { + Date date2delete = rc.getRelativeDate(now, numberOfPeriods); + + String filename = fileNamePattern.convertDate(date2delete); + + File file2Delete = new File(filename); + + if (file2Delete.exists() && file2Delete.isFile()) { + file2Delete.delete(); + } + } + +} Added: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithCleanTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithCleanTest.java Tue Oct 7 15:23:33 2008 @@ -0,0 +1,61 @@ +package ch.qos.logback.core.rolling; + + + + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.layout.EchoLayout; +import ch.qos.logback.core.util.Constants; + +public class TimeBasedRollingWithCleanTest { + + Context context = new ContextBase(); + EchoLayout<Object> layout = new EchoLayout<Object>(); + + static final String DATE_PATTERN = "yyyy-MM-dd_HH_mm_ss"; + + @Before + public void setUp() throws Exception { + context.setName("test"); + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void smoke() { + long currentTime = System.currentTimeMillis(); + + RollingFileAppender<Object> rfa = new RollingFileAppender<Object>(); + rfa.setContext(context); + rfa.setLayout(layout); + rfa.setFile(Constants.OUTPUT_DIR_PREFIX + "clean.txt"); + TimeBasedRollingPolicy tbrp = new TimeBasedRollingPolicy(); + tbrp.setContext(context); + tbrp.setFileNamePattern(Constants.OUTPUT_DIR_PREFIX + "clean-%d{" + + DATE_PATTERN + "}.txt"); + tbrp.setParent(rfa); + tbrp.setCurrentTime(currentTime); + tbrp.start(); + rfa.setRollingPolicy(tbrp); + rfa.start(); + + for (int i = 0; i < 10; i++) { + rfa.doAppend("Hello---" + i); + tbrp.setCurrentTime(addTime(tbrp.getCurrentTime(), 500)); + } + + } + + static long addTime(long currentTime, long timeToWait) { + return currentTime + timeToWait; + } + +} Modified: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java ============================================================================== --- logback/trunk/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java (original) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java Tue Oct 7 15:23:33 2008 @@ -9,7 +9,8 @@ */ package ch.qos.logback.core.rolling.helper; -import ch.qos.logback.core.rolling.helper.RollingCalendar; +import java.util.Date; + import junit.framework.TestCase; public class RollingCalendarTest extends TestCase { @@ -26,37 +27,65 @@ super.tearDown(); } - public void test() { + public void testPeriodicity() { { RollingCalendar rc = new RollingCalendar(); - assertEquals(RollingCalendar.TOP_OF_SECOND, rc - .computeTriggeringPeriod("yyyy-MM-dd_HH_mm_ss")); + assertEquals(PeriodicityType.TOP_OF_SECOND, rc + .computePeriodicity("yyyy-MM-dd_HH_mm_ss")); } { RollingCalendar rc = new RollingCalendar(); - assertEquals(RollingCalendar.TOP_OF_MINUTE, rc - .computeTriggeringPeriod("yyyy-MM-dd_HH_mm")); + assertEquals(PeriodicityType.TOP_OF_MINUTE, rc + .computePeriodicity("yyyy-MM-dd_HH_mm")); } - + { RollingCalendar rc = new RollingCalendar(); - assertEquals(RollingCalendar.TOP_OF_HOUR, rc - .computeTriggeringPeriod("yyyy-MM-dd_HH")); + assertEquals(PeriodicityType.TOP_OF_HOUR, rc + .computePeriodicity("yyyy-MM-dd_HH")); } - + { RollingCalendar rc = new RollingCalendar(); - assertEquals(RollingCalendar.TOP_OF_DAY, rc - .computeTriggeringPeriod("yyyy-MM-dd")); + assertEquals(PeriodicityType.TOP_OF_DAY, rc + .computePeriodicity("yyyy-MM-dd")); } - + { RollingCalendar rc = new RollingCalendar(); - assertEquals(RollingCalendar.TOP_OF_MONTH, rc - .computeTriggeringPeriod("yyyy-MM")); + assertEquals(PeriodicityType.TOP_OF_MONTH, rc + .computePeriodicity("yyyy-MM")); } - } + public void testVaryingNumberOfHourlyPeriods() { + RollingCalendar rc = new RollingCalendar(); + rc.init("yyyy-MM-dd_HH"); + long MILLIS_IN_HOUR = 3600*1000; + + for (int p = 100; p > -100; p--) { + long now = 1223325293589L; // Mon Oct 06 22:34:53 CEST 2008 + Date result = rc.getRelativeDate(new Date(now), p); + long expected = now - (now % (MILLIS_IN_HOUR)) + p * MILLIS_IN_HOUR; + assertEquals(expected, result.getTime()); + } + } + + public void testVaryingNumberOfDailyPeriods() { + RollingCalendar rc = new RollingCalendar(); + rc.init("yyyy-MM-dd"); + final long MILLIS_IN_DAY = 24*3600*1000; + + for (int p = 20; p > -100; p--) { + long now = 1223325293589L; // Mon Oct 06 22:34:53 CEST 2008 + Date nowDate = new Date(now); + Date result = rc.getRelativeDate(nowDate, p); + long offset = rc.getTimeZone().getRawOffset()+rc.getTimeZone().getDSTSavings(); + + long origin = now - (now % (MILLIS_IN_DAY)) - offset; + long expected = origin + p * MILLIS_IN_DAY; + assertEquals("p="+p, expected, result.getTime()); + } + } } Modified: logback/trunk/logback-site/src/site/pages/manual/appenders.html ============================================================================== --- logback/trunk/logback-site/src/site/pages/manual/appenders.html (original) +++ logback/trunk/logback-site/src/site/pages/manual/appenders.html Tue Oct 7 15:23:33 2008 @@ -925,21 +925,9 @@ <em>/wombat/foo.2006-24.log</em> until it is rolled over at the beginning of the next week. </td> - </tr> + </tr> <tr class="a"> <td class="small"> - <em>/wombat/foo. /<br/>%d{yyyy-MM-dd-a}.log</em> - </td> - <td>Rollover at midnight and midday of each day.</td> - - <td>During the first 12 hours of November 3rd, 2006, the logging - will be output to <em>/wombat/foo.2006-11-03-AM.log</em>. After - noon, and until midnight, the logging will be output to - <em>/wombat/foo.2006-11-03-PM.log</em>. - </td> - </tr> - <tr class="b"> - <td class="small"> <em>/wombat/foo. /<br/>%d{yyyy-MM-dd_HH}.log</em> </td> <td>Rollover at the top of each hour.</td> @@ -950,7 +938,7 @@ <em>/wombat/foo.2006-11-03_12.log</em>. </td> </tr> - <tr class="a"> + <tr class="b"> <td class="small"> <em>/wombat/foo. /<br/>%d{yyyy-MM-dd_HH-mm}.log</em> </td>