1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.rolling;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.nio.file.Files;
19  import java.nio.file.Paths;
20  import java.text.SimpleDateFormat;
21  import java.util.Date;
22  import java.util.List;
23  import java.util.concurrent.ExecutionException;
24  import java.util.function.UnaryOperator;
25  
26  import ch.qos.logback.core.CoreConstants;
27  import ch.qos.logback.core.util.CachingDateFormatter;
28  import ch.qos.logback.core.util.Duration;
29  import ch.qos.logback.core.util.StatusPrinter;
30  import org.junit.jupiter.api.BeforeEach;
31  import org.junit.jupiter.api.Test;
32  
33  import ch.qos.logback.core.encoder.EchoEncoder;
34  import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests;
35  import ch.qos.logback.core.status.InfoStatus;
36  import ch.qos.logback.core.status.StatusManager;
37  import ch.qos.logback.core.status.testUtil.StatusChecker;
38  import ch.qos.logback.core.util.FileSize;
39  
40  import static org.junit.jupiter.api.Assertions.assertEquals;
41  import static org.junit.jupiter.api.Assertions.assertFalse;
42  
43  public class SizeAndTimeBasedFNATP_Test extends ScaffoldingForRollingTests {
44      private SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object> sizeAndTimeBasedFNATP = null;
45      private RollingFileAppender<Object> rfa1 = new RollingFileAppender<Object>();
46      private TimeBasedRollingPolicy<Object> tbrp1 = new TimeBasedRollingPolicy<Object>();
47      private RollingFileAppender<Object> rfa2 = new RollingFileAppender<Object>();
48      private TimeBasedRollingPolicy<Object> tbrp2 = new TimeBasedRollingPolicy<Object>();
49  
50      private EchoEncoder<Object> encoder = new EchoEncoder<Object>();
51      int fileSize = 0;
52      int fileIndexCounter = 0;
53      int sizeThreshold = 0;
54  
55      @BeforeEach
56      public void setUp() {
57          super.setUp();
58      }
59  
60      private void initRollingFileAppender(RollingFileAppender<Object> rfa, String filename) {
61          rfa.setContext(context);
62          rfa.setEncoder(encoder);
63          if (filename != null) {
64              rfa.setFile(filename);
65          }
66      }
67  
68      private void initPolicies(RollingFileAppender<Object> rfa, TimeBasedRollingPolicy<Object> tbrp,
69              String filenamePattern, int sizeThreshold, long givenTime, long lastCheck) {
70          sizeAndTimeBasedFNATP = new SizeAndTimeBasedFileNamingAndTriggeringPolicy<Object>();
71          sizeAndTimeBasedFNATP.setContext(context);
72          sizeAndTimeBasedFNATP.setCheckIncrement(Duration.buildByMilliseconds(10));
73          tbrp.setContext(context);
74          sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(sizeThreshold));
75          tbrp.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP);
76          tbrp.setFileNamePattern(filenamePattern);
77          tbrp.setParent(rfa);
78          tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(givenTime);
79          rfa.setRollingPolicy(tbrp);
80          tbrp.start();
81          rfa.start();
82      }
83  
84      private void addExpectedFileNamedIfItsTime(String randomOutputDir, String testId, String msg,
85              String compressionSuffix) {
86          fileSize = fileSize + msg.getBytes().length;
87          if (passThresholdTime(nextRolloverThreshold)) {
88              fileIndexCounter = 0;
89              fileSize = 0;
90              addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(),
91                      fileIndexCounter, compressionSuffix);
92              recomputeRolloverThreshold(currentTime);
93              return;
94          }
95  
96          // windows can delay file size changes, so we only allow for fileIndexCounter 0
97          if ((fileIndexCounter == 0) && fileSize > sizeThreshold) {
98              addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(),
99                      fileIndexCounter, compressionSuffix);
100             fileIndexCounter = fileIndexCounter + 1;
101             fileSize = 0;
102         }
103     }
104 
105     void generic(String testId, UnaryOperator<String> filenameFunction, boolean withSecondPhase,
106             String compressionSuffix) throws IOException, InterruptedException, ExecutionException {
107         String file = filenameFunction.apply(testId);
108         initRollingFileAppender(rfa1, file);
109         sizeThreshold = 300;
110 
111         initPolicies(rfa1, tbrp1,
112                 randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}-%i.txt" + compressionSuffix,
113                 sizeThreshold, currentTime, 0);
114         addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(),
115                 fileIndexCounter, compressionSuffix);
116         incCurrentTime(100);
117         tbrp1.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
118         int runLength = 100;
119         String prefix = "Hello -----------------";
120 
121         for (int i = 0; i < runLength; i++) {
122             String msg = prefix + i;
123             rfa1.doAppend(msg);
124             addExpectedFileNamedIfItsTime(randomOutputDir, testId, msg, compressionSuffix);
125             incCurrentTime(20);
126             tbrp1.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
127             add(tbrp1.compressionFuture);
128             add(tbrp1.cleanUpFuture);
129         }
130 
131         if (withSecondPhase) {
132             secondPhase(testId, filenameFunction, compressionSuffix, runLength, prefix);
133             runLength = runLength * 2;
134         }
135 
136         if (file != null)
137             massageExpectedFilesToCorresponToCurrentTarget(testId, this::testId2FileName);
138 
139         Thread.yield();
140         // wait for compression to finish
141         waitForJobsToComplete();
142 
143         //StatusPrinter.print(context);
144         existenceCheck(expectedFilenameList);
145         sortedContentCheck(randomOutputDir, runLength, prefix);
146     }
147 
148     void secondPhase(String testId, UnaryOperator<String> filenameFunction, String compressionSuffix, int runLength,
149             String prefix) {
150         rfa1.stop();
151 
152         String filename = filenameFunction.apply(testId);
153         if (filename != null) {
154             File f = new File(filename);
155             f.setLastModified(currentTime);
156         }
157 
158         StatusManager sm = context.getStatusManager();
159         sm.add(new InfoStatus("Time when rfa1 is stopped: " + new Date(currentTime), this));
160         sm.add(new InfoStatus("currentTime%1000=" + (currentTime % 1000), this));
161 
162         initRollingFileAppender(rfa2, filename);
163         initPolicies(rfa2, tbrp2,
164                 randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}-%i.txt" + compressionSuffix,
165                 sizeThreshold, currentTime, 0);
166 
167         for (int i = runLength; i < runLength * 2; i++) {
168             incCurrentTime(100);
169             tbrp2.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
170             String msg = prefix + i;
171             rfa2.doAppend(msg);
172             addExpectedFileNamedIfItsTime(randomOutputDir, testId, msg, compressionSuffix);
173         }
174     }
175 
176     static final boolean FIRST_PHASE_ONLY = false;
177     static final boolean WITH_SECOND_PHASE = true;
178     static final String DEFAULT_COMPRESSION_SUFFIX = "";
179 
180     @Test
181     public void noCompression_FileSet_NoRestart_1() throws InterruptedException, ExecutionException, IOException {
182         generic("test1", this::testId2FileName, FIRST_PHASE_ONLY, DEFAULT_COMPRESSION_SUFFIX);
183     }
184 
185     @Test
186     public void noCompression_FileBlank_NoRestart_2() throws Exception {
187         generic("test2", this::nullFileName, FIRST_PHASE_ONLY, DEFAULT_COMPRESSION_SUFFIX);
188     }
189 
190     @Test
191     public void noCompression_FileBlank_WithStopStart_3() throws Exception {
192         generic("test3", this::nullFileName, WITH_SECOND_PHASE, DEFAULT_COMPRESSION_SUFFIX);
193     }
194 
195     @Test
196     public void noCompression_FileSet_WithStopStart_4() throws Exception {
197         generic("test4", this::testId2FileName, WITH_SECOND_PHASE, DEFAULT_COMPRESSION_SUFFIX);
198     }
199 
200     @Test
201     public void withGZCompression_FileSet_NoRestart_5() throws Exception {
202         generic("test5", this::testId2FileName, FIRST_PHASE_ONLY, ".gz");
203     }
204 
205     @Test
206     public void withGZCompression_FileBlank_NoRestart_6() throws Exception {
207         generic("test6", this::nullFileName, FIRST_PHASE_ONLY, ".gz");
208     }
209 
210     @Test
211     public void withZipCompression_FileSet_NoRestart_7() throws Exception {
212         generic("test7", this::testId2FileName, FIRST_PHASE_ONLY, ".zip");
213         List<String> zipFiles = filterElementsInListBySuffix(".zip");
214         checkZipEntryMatchesZipFilename(zipFiles);
215     }
216 
217     @Test
218     public void checkMissingIntToken() {
219         String stem = "toto.log";
220         String testId = "checkMissingIntToken";
221         String compressionSuffix = "gz";
222 
223         String file = (stem != null) ? randomOutputDir + stem : null;
224         initRollingFileAppender(rfa1, file);
225         sizeThreshold = 300;
226         initPolicies(rfa1, tbrp1,
227                 randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}.txt" + compressionSuffix,
228                 sizeThreshold, currentTime, 0);
229 
230         // StatusPrinter.print(context);
231         assertFalse(rfa1.isStarted());
232         StatusChecker checker = new StatusChecker(context);
233         checker.assertContainsMatch("Missing integer token");
234     }
235 
236     @Test
237     public void checkDateCollision() {
238         String stem = "toto.log";
239         String testId = "checkDateCollision";
240         String compressionSuffix = "gz";
241 
242         String file = (stem != null) ? randomOutputDir + stem : null;
243         initRollingFileAppender(rfa1, file);
244         sizeThreshold = 300;
245         initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{EE}.txt" + compressionSuffix, sizeThreshold,
246                 currentTime, 0);
247 
248         // StatusPrinter.print(context);
249         assertFalse(rfa1.isStarted());
250         StatusChecker checker = new StatusChecker(context);
251         checker.assertContainsMatch("The date format in FileNamePattern");
252     }
253 
254     @Test
255     public void checkInitialFileSize_withFile() throws IOException {
256         String stem = "foo.log";
257         String testId = "checkDateCollision";
258         String fixedContent = "Hello world";
259         byte[] fixedContentBytes = fixedContent.getBytes();
260 
261         String fileProperty = randomOutputDir + stem;
262         Files.createDirectories(Paths.get(randomOutputDir));
263         Files.write(Paths.get(fileProperty), fixedContentBytes);
264 
265         initRollingFileAppender(rfa1, fileProperty);
266         sizeThreshold = 300;
267         initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d-%i.txt", sizeThreshold,
268                         currentTime, 0);
269 
270         //StatusPrinter.print(context);
271 
272         assertEquals(fixedContentBytes.length, tbrp1.getLengthCounter().getLength());
273     }
274 
275 
276     @Test
277     public void checkInitialFileSize_withoutFile() throws IOException {
278         String testId = "checkInitialFileSize_withoutFile";
279         String fixedContent = "Hello world";
280         byte[] fixedContentBytes = fixedContent.getBytes();
281 
282 
283         CachingDateFormatter cdf = new CachingDateFormatter(CoreConstants.DAILY_DATE_PATTERN);
284         String nowString = cdf.format(currentTime);
285         String pathToFirstFile = randomOutputDir + testId + "-"+nowString+"-0.txt";
286 
287         Files.createDirectories(Paths.get(randomOutputDir));
288         Files.write(Paths.get(pathToFirstFile), fixedContentBytes);
289 
290 
291         initRollingFileAppender(rfa1, null);
292         sizeThreshold = 300;
293         initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d-%i.txt", sizeThreshold,
294                         currentTime, 0);
295 
296         StatusPrinter.print(context);
297 
298         assertEquals(fixedContentBytes.length, tbrp1.getLengthCounter().getLength());
299     }
300 
301     // @Test
302     // public void testHistoryAsFileCount() throws IOException {
303     // String testId = "testHistoryAsFileCount";
304     // int maxHistory = 10;
305     // initRollingFileAppender(rfa1, randomOutputDir + "~" + testId);
306     // sizeThreshold = 50;
307     // System.out.println("testHistoryAsFileCount started on "+new
308     // Date(currentTime));
309     // initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{" +
310     // DATE_PATTERN_WITH_SECONDS + "}-%i.txt",
311     // sizeThreshold, currentTime, 0, maxHistory, true);
312     //
313     // incCurrentTime(100);
314     // tbrp1.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
315     // int runLength = 1000;
316     //
317     // for (int i = 0; i < runLength; i++) {
318     // String msg = "" + i;
319     // rfa1.doAppend(msg);
320     // incCurrentTime(20);
321     // tbrp1.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime);
322     // add(tbrp1.future);
323     // }
324     //
325     // Thread.yield();
326     // // wait for compression to finish
327     // waitForJobsToComplete();
328     //
329     // assertEquals(maxHistory + 1, getFilesInDirectory(randomOutputDir).length);
330     // sortedContentCheck(randomOutputDir, 1000, "", 863);
331     // }
332 }