svn commit: r1820 - in logback/trunk: logback-core/src/main/java/ch/qos/logback/core/rolling logback-core/src/main/java/ch/qos/logback/core/rolling/helper logback-core/src/test/java/ch/qos/logback/core/rolling logback-core/src/test/java/ch/qos/logback/core/rolling/helper logback-site/src/site/pages/manual

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>

noreply.ceki@qos.ch skrev:
+import sun.misc.Cleaner; +
This caught my eye. Is this a standard class these days? /Thorbjørn

Thorbjørn, Good catch. Thank you. I had added an interface called Cleaner to remove old archive files. However, since it has only one implementation, decided to remove it. At some point, Eclipse thought that sun.misc.Cleaner instead of ch.qos....Cleaner was referenced hence the erroneous import. Anyway, this issue has been already fixed in 1822. See http://tinyurl.com/3offjp for exact details. I appreciate the warning though. Cheers, Thorbjørn Ravn Andersen wrote:
noreply.ceki@qos.ch skrev:
+import sun.misc.Cleaner; +
This caught my eye. Is this a standard class these days?
/Thorbjørn
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch

Ceki Gulcu skrev:
Thorbjørn,
Good catch. Thank you. I had added an interface called Cleaner to remove old archive files. However, since it has only one implementation, decided to remove it. At some point, Eclipse thought that sun.misc.Cleaner instead of ch.qos....Cleaner was referenced hence the erroneous import.
Ah, the joy of just typing Ctrl-Shift-O to make things shut up :) Which reminds me that I should see if slf4j compiles with a clean room JVM. I have it on my TODO list :)
participants (3)
-
Ceki Gulcu
-
noreply.ceki@qos.ch
-
Thorbjørn Ravn Andersen