1   /**
2    * LOGBack: the generic, reliable, fast and flexible logging framework.
3    * 
4    * Copyright (C) 1999-2006, QOS.ch
5    * 
6    * This library is free software, you can redistribute it and/or modify it under
7    * the terms of the GNU Lesser General Public License as published by the Free
8    * Software Foundation.
9    */
10  
11  package ch.qos.logback.core.rolling;
12  
13  import static org.junit.Assert.assertTrue;
14  
15  import java.io.File;
16  import java.sql.Date;
17  import java.text.SimpleDateFormat;
18  import java.util.ArrayList;
19  import java.util.Calendar;
20  import java.util.List;
21  import java.util.concurrent.TimeUnit;
22  
23  import org.junit.After;
24  import org.junit.Before;
25  import org.junit.Test;
26  
27  import ch.qos.logback.core.Context;
28  import ch.qos.logback.core.ContextBase;
29  import ch.qos.logback.core.layout.EchoLayout;
30  import ch.qos.logback.core.util.Compare;
31  import ch.qos.logback.core.util.Constants;
32  
33  /**
34   * A rather exhaustive set of tests. Tests include leaving the file option
35   * blank, or setting it, with and without compression, and tests with or without
36   * stopping/restarting the RollingFileAppender.
37   * 
38   * The regression tests log a few times using a RollingFileAppender. Then, they
39   * predict the names of the files which should be generated and compare them
40   * with witness files.
41   * 
42   * <pre>
43   *               Compression     file option    Stop/Restart 
44   *    Test1      NO              BLANK           NO
45   *    Test2      NO              BLANK           YES
46   *    Test3      YES             BLANK           NO
47   *    Test4      NO              SET             YES 
48   *    Test5      NO              SET             NO
49   *    Test6      YES             SET             NO
50   * </pre>
51   * 
52   * @author Ceki G&uuml;lc&uuml;
53   */
54  public class TimeBasedRollingTest {
55  
56    static final String DATE_PATTERN = "yyyy-MM-dd_HH_mm_ss";
57    SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN);
58  
59    EchoLayout<Object> layout = new EchoLayout<Object>();
60    Context context = new ContextBase();
61  
62    RollingFileAppender<Object> rfa1 = new RollingFileAppender<Object>();
63    TimeBasedRollingPolicy tbrp1 = new TimeBasedRollingPolicy();
64  
65    RollingFileAppender<Object> rfa2 = new RollingFileAppender<Object>();
66    TimeBasedRollingPolicy tbrp2 = new TimeBasedRollingPolicy();
67  
68    Calendar cal = Calendar.getInstance();
69    long currentTime; // initialized in setUp()
70    long nextRolloverThreshold; // initialized in setUp()
71    List<String> expectedFilenameList = new ArrayList<String>();
72  
73    @Before
74    public void setUp() {
75      context.setName("test");
76      cal.set(Calendar.MILLISECOND, 333);
77      currentTime = cal.getTimeInMillis();
78      recomputeRolloverThreshold(currentTime);
79      System.out.println("at setUp() currentTime=" + sdf.format(new Date(currentTime)));
80  
81      // Delete .log files
82      deleteStaleLogFile("test4.log");
83      deleteStaleLogFile("test4B.log");
84      deleteStaleLogFile("test5.log");
85      deleteStaleLogFile("test6.log");
86    }
87  
88    void deleteStaleLogFile(String filename) {
89      File target = new File(Constants.OUTPUT_DIR_PREFIX + filename);
90      target.mkdirs();
91      target.delete();
92    }
93  
94    @After
95    public void tearDown() {
96    }
97  
98    void initRFA(RollingFileAppender<Object> rfa, String filename) {
99      rfa.setContext(context);
100     rfa.setLayout(layout);
101     if (filename != null) {
102       rfa.setFile(filename);
103     }
104   }
105 
106   void initTRBP(RollingFileAppender<Object> rfa, TimeBasedRollingPolicy tbrp,
107       String filenamePattern, long givenTime, long lastCheck) {
108     tbrp.setContext(context);
109     tbrp.setFileNamePattern(filenamePattern);
110     tbrp.setParent(rfa);
111     tbrp.setCurrentTime(givenTime);
112     if (lastCheck != 0) {
113       tbrp.setLastCheck(new Date(lastCheck));
114     }
115     rfa.setRollingPolicy(tbrp);
116     tbrp.start();
117     rfa.start();
118   }
119 
120   /**
121    * Test rolling without compression, file option left blank, no stop/start
122    */
123   @Test
124   public void noCompression_FileBlank_NoRestart_1() throws Exception {
125     String testId = "test1";
126     initRFA(rfa1, null);
127     initTRBP(rfa1, tbrp1, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
128         + DATE_PATTERN + "}", currentTime, 0);
129 
130     // compute the current filename
131     addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(), false);
132 
133     incCurrentTime(1100);
134     tbrp1.setCurrentTime(currentTime);
135 
136     for (int i = 0; i < 3; i++) {
137       rfa1.doAppend("Hello---" + i);
138       addExpectedFileNamedIfItsTime_ByDate(testId, false);
139       incCurrentTime(500);
140       tbrp1.setCurrentTime(currentTime);
141     }
142 
143     int i = 0;
144     for (String fn : expectedFilenameList) {
145       assertTrue(Compare.compare(fn, Constants.TEST_DIR_PREFIX
146           + "witness/rolling/tbr-" + testId + "." + i++));
147     }
148   }
149 
150   /**
151    * No compression, file option left blank, with stop/restart,
152    */
153   @Test
154   public void noCompression_FileBlank_StopRestart_2() throws Exception {
155     String testId = "test2";
156 
157     initRFA(rfa1, null);
158     initTRBP(rfa1, tbrp1, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
159         + DATE_PATTERN + "}", currentTime, 0);
160 
161     // a new file is created by virtue of rfa.start();
162     addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(), false);
163 
164     incCurrentTime(1100);
165     tbrp1.setCurrentTime(currentTime);
166 
167     for (int i = 0; i <= 2; i++) {
168       rfa1.doAppend("Hello---" + i);
169       addExpectedFileNamedIfItsTime_ByDate(testId, false);
170       incCurrentTime(500);
171       tbrp1.setCurrentTime(currentTime);
172     }
173 
174     rfa1.stop();
175 
176     initRFA(rfa2, null);
177     initTRBP(rfa2, tbrp2, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
178         + DATE_PATTERN + "}", tbrp1.getCurrentTime(), 0);
179 
180     for (int i = 0; i <= 2; i++) {
181       addExpectedFileNamedIfItsTime_ByDate(testId, false);
182       rfa2.doAppend("World---" + i);
183       incCurrentTime(100);
184       tbrp2.setCurrentTime(currentTime);
185     }
186 
187     int i = 0;
188     for (String fn : expectedFilenameList) {
189       assertTrue(Compare.compare(fn, Constants.TEST_DIR_PREFIX
190           + "witness/rolling/tbr-" + testId + "." + i++));
191     }
192   }
193 
194   /**
195    * With compression, file option left blank, no stop/restart
196    */
197   @Test
198   public void withCompression_FileBlank_NoRestart_3() throws Exception {
199     String testId = "test3";
200     initRFA(rfa1, null);
201     initTRBP(rfa1, tbrp1, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
202         + DATE_PATTERN + "}.gz", currentTime, 0);
203 
204     addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(), true);
205     incCurrentTime(1100);
206     tbrp1.setCurrentTime(currentTime);
207 
208     for (int i = 0; i < 3; i++) {
209       // when i == 2, file name should not have .gz extension
210       addExpectedFileNamedIfItsTime_ByDate(testId, i != 2);
211       rfa1.doAppend("Hello---" + i);
212       incCurrentTime(500);
213       tbrp1.setCurrentTime(currentTime);
214     }
215 
216     tbrp1.future.get(2000, TimeUnit.MILLISECONDS);
217 
218     int i = 0;
219     for (String fn : expectedFilenameList) {
220       assertTrue(Compare.compare(fn, Constants.TEST_DIR_PREFIX
221           + "witness/rolling/tbr-" + testId + "." + i + addGZIfNotLast(i)));
222       i++;
223     }
224   }
225 
226   /**
227    * Without compression, file option set, with stop/restart
228    */
229   @Test
230   public void noCompression_FileSet_StopRestart_4() throws Exception {
231     String testId = "test4";
232     initRFA(rfa1, testId2FileName(testId));
233     initTRBP(rfa1, tbrp1, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
234         + DATE_PATTERN + "}", currentTime, 0);
235 
236     addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(), false);
237 
238     incCurrentTime(1100);
239     tbrp1.setCurrentTime(currentTime);
240 
241     for (int i = 0; i <= 2; i++) {
242       rfa1.doAppend("Hello---" + i);
243       addExpectedFileNamedIfItsTime_ByDate(testId, false);
244       incCurrentTime(500);
245       tbrp1.setCurrentTime(currentTime);
246     }
247 
248     rfa1.stop();
249 
250     initRFA(rfa2, testId2FileName(testId));
251     initTRBP(rfa2, tbrp2, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
252         + DATE_PATTERN + "}", currentTime, currentTime);
253 
254     for (int i = 0; i <= 2; i++) {
255       rfa2.doAppend("World---" + i);
256       addExpectedFileNamedIfItsTime_ByDate(testId, false);
257       incCurrentTime(100);
258       tbrp2.setCurrentTime(currentTime);
259     }
260 
261     massageExpectedFilesToCorresponToCurrentTarget("test4.log");
262 
263     int i = 0;
264     for (String fn : expectedFilenameList) {
265       assertTrue(Compare.compare(fn, Constants.TEST_DIR_PREFIX
266           + "witness/rolling/tbr-" + testId + "." + i++));
267     }
268   }
269 
270   @Test
271   public void noCompression_FileSet_StopRestart_WithLongWait_4B()
272       throws Exception {
273     String testId = "test4B";
274     initRFA(rfa1, testId2FileName(testId));
275     initTRBP(rfa1, tbrp1, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
276         + DATE_PATTERN + "}", currentTime, 0);
277 
278     addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(), false);
279 
280     incCurrentTime(1100);
281     tbrp1.setCurrentTime(currentTime);
282 
283     for (int i = 0; i <= 2; i++) {
284       rfa1.doAppend("Hello---" + i);
285       addExpectedFileNamedIfItsTime_ByDate(testId, false);
286       incCurrentTime(500);
287       tbrp1.setCurrentTime(currentTime);
288     }
289 
290     rfa1.stop();
291 
292     long fileTimestamp = currentTime;
293     incCurrentTime(2000);
294 
295     initRFA(rfa2, Constants.OUTPUT_DIR_PREFIX + "test4B.log");
296     initTRBP(rfa2, tbrp2, Constants.OUTPUT_DIR_PREFIX + testId +"-%d{"
297         + DATE_PATTERN + "}", currentTime, fileTimestamp);
298 
299     for (int i = 0; i <= 2; i++) {
300       rfa2.doAppend("World---" + i);
301       addExpectedFileNamedIfItsTime_ByDate(testId, false);
302       incCurrentTime(100);
303       tbrp2.setCurrentTime(currentTime);
304     }
305 
306     massageExpectedFilesToCorresponToCurrentTarget("test4B.log");
307 
308     int i = 0;
309     for (String fn : expectedFilenameList) {
310       assertTrue(Compare.compare(fn, Constants.TEST_DIR_PREFIX
311           + "witness/rolling/tbr-test4B." + i++));
312     }
313 
314   }
315 
316   /**
317    * No compression, file option set, without stop/restart
318    */
319   @Test
320   public void noCompression_FileSet_NoRestart_5() throws Exception {
321     String testId = "test5";
322 
323     initRFA(rfa1, testId2FileName(testId));
324     initTRBP(rfa1, tbrp1, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
325         + DATE_PATTERN + "}", currentTime, 0);
326 
327     addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(), false);
328 
329     incCurrentTime(1100);
330     tbrp1.setCurrentTime(currentTime);
331 
332     for (int i = 0; i < 3; i++) {
333       rfa1.doAppend("Hello---" + i);
334       addExpectedFileNamedIfItsTime_ByDate(testId, false);
335       incCurrentTime(500);
336       tbrp1.setCurrentTime(currentTime);
337     }
338 
339     massageExpectedFilesToCorresponToCurrentTarget("test5.log");
340 
341     int i = 0;
342     for (String fn : expectedFilenameList) {
343       assertTrue(Compare.compare(fn, Constants.TEST_DIR_PREFIX
344           + "witness/rolling/tbr-test5." + i++));
345     }
346   }
347 
348   /**
349    * With compression, file option set, no stop/restart,
350    */
351   @Test
352   public void withCompression_FileSet_NoRestart_6() throws Exception {
353 
354     String testId = "test6";
355 
356     initRFA(rfa1, testId2FileName(testId));
357     initTRBP(rfa1, tbrp1, Constants.OUTPUT_DIR_PREFIX + testId + "-%d{"
358         + DATE_PATTERN + "}.gz", currentTime, 0);
359 
360     addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(), true);
361 
362     incCurrentTime(1100);
363     tbrp1.setCurrentTime(currentTime);
364 
365     for (int i = 0; i < 3; i++) {
366       rfa1.doAppend("Hello---" + i);
367       addExpectedFileNamedIfItsTime_ByDate(testId, true);
368       incCurrentTime(500);
369       tbrp1.setCurrentTime(currentTime);
370     }
371 
372     // wait for the compression task to finish
373     tbrp1.future.get(1000, TimeUnit.MILLISECONDS);
374 
375     massageExpectedFilesToCorresponToCurrentTarget("test6.log");
376 
377     int i = 0;
378     for (String fn : expectedFilenameList) {
379       assertTrue(Compare.compare(fn, Constants.TEST_DIR_PREFIX
380           + "witness/rolling/tbr-" + testId + "." + i + addGZIfNotLast(i)));
381       i++;
382     }
383   }
384 
385   // =========================================================================
386   // utility methods
387   // =========================================================================
388 
389   String testId2FileName(String testId) {
390     return Constants.OUTPUT_DIR_PREFIX + testId + ".log";
391   }
392 
393   void massageExpectedFilesToCorresponToCurrentTarget(String file) {
394     // we added one too many files by date
395     expectedFilenameList.remove(expectedFilenameList.size() - 1);
396     // since file is set, we have to add it
397     addExpectedFileName_ByFile(file);
398   }
399 
400   String addGZIfNotLast(int i) {
401     int lastIndex = expectedFilenameList.size() - 1;
402     if (i != lastIndex) {
403       return ".gz";
404     } else {
405       return "";
406     }
407   }
408 
409   void addExpectedFileName_ByDate(String testId, Date date, boolean gzExtension) {
410     String fn = Constants.OUTPUT_DIR_PREFIX + testId + "-" + sdf.format(date);
411     if (gzExtension) {
412       fn += ".gz";
413     }
414     expectedFilenameList.add(fn);
415   }
416 
417   void addExpectedFileNamedIfItsTime_ByDate(String testId, boolean gzExtension) {
418     if (passThresholdTime(nextRolloverThreshold)) {
419       addExpectedFileName_ByDate(testId, getDateOfCurrentPeriodsStart(),
420           gzExtension);
421       recomputeRolloverThreshold(currentTime);
422     }
423   }
424 
425   void addExpectedFileName_ByFile(String filenameSuffix) {
426     String fn = Constants.OUTPUT_DIR_PREFIX + filenameSuffix;
427     expectedFilenameList.add(fn);
428   }
429 
430   Date getDateOfCurrentPeriodsStart() {
431     long delta = currentTime % 1000;
432     return new Date(currentTime - delta);
433   }
434 
435   Date getDateOfPastPeriodsStart() {
436     long delta = currentTime % 1000;
437     return new Date(currentTime - delta - 1000);
438   }
439 
440   static long addTime(long currentTime, long timeToWait) {
441     return currentTime + timeToWait;
442   }
443 
444   boolean passThresholdTime(long nextRolloverThreshold) {
445     return currentTime >= nextRolloverThreshold;
446   }
447 
448   // assuming rollover every second
449   void recomputeRolloverThreshold(long ct) {
450     long delta = ct % 1000;
451     nextRolloverThreshold = (ct - delta) + 1000;
452   }
453 
454   void incCurrentTime(long increment) {
455     currentTime += increment;
456   }
457 
458   void printLongAsDate(String msg, long time) {
459     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss");
460     System.out.println(msg + sdf.format(new Date(time)));
461   }
462 }