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 ch.qos.logback.core.util.Duration;
17  import ch.qos.logback.core.util.FileSize;
18  import ch.qos.logback.core.util.FileUtil;
19  import org.junit.jupiter.api.AfterEach;
20  import org.junit.jupiter.api.Assertions;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.DisplayName;
23  import org.junit.jupiter.api.Test;
24  
25  import ch.qos.logback.core.Appender;
26  import ch.qos.logback.core.Context;
27  import ch.qos.logback.core.ContextBase;
28  import ch.qos.logback.core.appender.AbstractAppenderTest;
29  import ch.qos.logback.core.testUtil.DummyEncoder;
30  import ch.qos.logback.core.status.Status;
31  import ch.qos.logback.core.testUtil.CoreTestConstants;
32  import ch.qos.logback.core.testUtil.RandomUtil;
33  import ch.qos.logback.core.status.testUtil.StatusChecker;
34  
35  import java.io.File;
36  import java.io.FileInputStream;
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.nio.file.Files;
40  import java.util.Collections;
41  import java.util.List;
42  //import ch.qos.logback.core.util.StatusPrinter;
43  
44  public class RollingFileAppenderTest extends AbstractAppenderTest<Object> {
45  
46      RollingFileAppender<Object> rfa = new RollingFileAppender<Object>();
47      Context context = new ContextBase();
48  
49      TimeBasedRollingPolicy<Object> tbrp = new TimeBasedRollingPolicy<Object>();
50      int diff = RandomUtil.getPositiveInt();
51      String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/";
52      DummyEncoder<Object> encoder;
53  
54      @BeforeEach
55      public void setUp() throws Exception {
56          // noStartTest fails if the context is set in setUp
57          // rfa.setContext(context);
58          encoder = new DummyEncoder<>();
59          rfa.setEncoder(encoder);
60          rfa.setName("test");
61          tbrp.setContext(context);
62          tbrp.setParent(rfa);
63      }
64  
65      @AfterEach
66      public void tearDown() throws Exception {
67      }
68  
69      @Override
70      protected Appender<Object> getAppender() {
71          return rfa;
72      }
73  
74      @Override
75      protected Appender<Object> getConfiguredAppender() {
76          rfa.setContext(context);
77          tbrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "toto-%d.log");
78          tbrp.start();
79          rfa.setRollingPolicy(tbrp);
80  
81          rfa.start();
82          return rfa;
83      }
84  
85      @Test
86      public void testPrudentModeLogicalImplications() {
87          rfa.setContext(context);
88          // prudent mode will force "file" property to be null
89          rfa.setFile("some non null value");
90          rfa.setAppend(false);
91          rfa.setPrudent(true);
92  
93          tbrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "toto-%d.log");
94          tbrp.start();
95          rfa.setRollingPolicy(tbrp);
96  
97          rfa.start();
98  
99          Assertions.assertTrue(rfa.isAppend());
100         Assertions.assertNull(rfa.rawFileProperty());
101         Assertions.assertTrue(rfa.isStarted());
102     }
103 
104     @Test
105     public void testPrudentModeLogicalImplicationsOnCompression() {
106         rfa.setContext(context);
107         rfa.setAppend(false);
108         rfa.setPrudent(true);
109 
110         tbrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "toto-%d.log.zip");
111         tbrp.start();
112         rfa.setRollingPolicy(tbrp);
113 
114         rfa.start();
115 
116         StatusChecker checker = new StatusChecker(context);
117         Assertions.assertFalse(rfa.isStarted());
118         Assertions.assertEquals(Status.ERROR, checker.getHighestLevel(0));
119     }
120 
121     @Test
122     public void testFilePropertyAfterRollingPolicy() {
123         rfa.setContext(context);
124         rfa.setRollingPolicy(tbrp);
125         rfa.setFile("x");
126         // StatusPrinter.print(context);
127         StatusChecker statusChecker = new StatusChecker(context.getStatusManager());
128         statusChecker.assertContainsMatch(Status.ERROR, "File property must be set before any triggeringPolicy ");
129     }
130 
131     @Test
132     public void testFilePropertyAfterTriggeringPolicy() {
133         rfa.setContext(context);
134         rfa.setTriggeringPolicy(new SizeBasedTriggeringPolicy<Object>());
135         rfa.setFile("x");
136         StatusChecker statusChecker = new StatusChecker(context.getStatusManager());
137         statusChecker.assertContainsMatch(Status.ERROR, "File property must be set before any triggeringPolicy ");
138     }
139 
140     @Test
141     public void testFileNameWithParenthesis() {
142         // if ')' is not escaped, the test throws
143         // java.lang.IllegalStateException: FileNamePattern
144         // [.../program(x86)/toto-%d.log] does not contain a valid
145         // DateToken
146         rfa.setContext(context);
147         tbrp.setFileNamePattern(randomOutputDir + "program(x86)/toto-%d.log");
148         tbrp.start();
149         rfa.setRollingPolicy(tbrp);
150         rfa.start();
151         rfa.doAppend("hello");
152     }
153 
154     @Test
155     public void stopTimeBasedRollingPolicy() {
156         rfa.setContext(context);
157 
158         tbrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "toto-%d.log.zip");
159         tbrp.start();
160         rfa.setRollingPolicy(tbrp);
161         rfa.start();
162 
163         //StatusPrinter.print(context);
164         Assertions.assertTrue(tbrp.isStarted());
165         Assertions.assertTrue(rfa.isStarted());
166         rfa.stop();
167         Assertions.assertFalse(rfa.isStarted());
168         Assertions.assertFalse(tbrp.isStarted());
169 
170     }
171 
172     @Test
173     public void stopFixedWindowRollingPolicy() {
174         rfa.setContext(context);
175         rfa.setFile(CoreTestConstants.OUTPUT_DIR_PREFIX + "toto-.log");
176 
177         FixedWindowRollingPolicy fwRollingPolicy = new FixedWindowRollingPolicy();
178         fwRollingPolicy.setContext(context);
179         fwRollingPolicy.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "toto-%i.log.zip");
180         fwRollingPolicy.setParent(rfa);
181         fwRollingPolicy.start();
182         SizeBasedTriggeringPolicy<Object> sbTriggeringPolicy = new SizeBasedTriggeringPolicy<Object>();
183         sbTriggeringPolicy.setContext(context);
184         sbTriggeringPolicy.start();
185 
186         rfa.setRollingPolicy(fwRollingPolicy);
187         rfa.setTriggeringPolicy(sbTriggeringPolicy);
188 
189         rfa.start();
190 
191         // StatusPrinter.print(context);
192         Assertions.assertTrue(fwRollingPolicy.isStarted());
193         Assertions.assertTrue(sbTriggeringPolicy.isStarted());
194         Assertions.assertTrue(rfa.isStarted());
195         rfa.stop();
196         Assertions.assertFalse(rfa.isStarted());
197         Assertions.assertFalse(fwRollingPolicy.isStarted());
198         Assertions.assertFalse(sbTriggeringPolicy.isStarted());
199 
200     }
201 
202     /**
203      * Test for http://jira.qos.ch/browse/LOGBACK-796
204      */
205     @Test
206     public void testFileShouldNotMatchFileNamePattern() {
207         rfa.setContext(context);
208         rfa.setFile(CoreTestConstants.OUTPUT_DIR_PREFIX + "x-2013-04.log");
209         tbrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "x-%d{yyyy-MM}.log");
210         tbrp.start();
211 
212         rfa.setRollingPolicy(tbrp);
213         rfa.start();
214         StatusChecker statusChecker = new StatusChecker(context);
215         final String msg = "File property collides with fileNamePattern. Aborting.";
216         boolean containsMatch = statusChecker.containsMatch(Status.ERROR, msg);
217         Assertions.assertTrue(containsMatch, "Missing error: " + msg);
218     }
219 
220     @Test
221     public void collidingTimeformat() {
222         rfa.setContext(context);
223         rfa.setAppend(false);
224         rfa.setPrudent(true);
225 
226         tbrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "toto-%d{dd}.log.zip");
227         tbrp.start();
228         rfa.setRollingPolicy(tbrp);
229 
230         rfa.start();
231 
232         StatusChecker checker = new StatusChecker(context);
233         Assertions.assertFalse(rfa.isStarted());
234         Assertions.assertEquals(Status.ERROR, checker.getHighestLevel(0));
235         // StatusPrinter.print(context);
236         checker.assertContainsMatch("The date format in FileNamePattern will result");
237     }
238 
239     @Test
240     public void collidingFileNamePattern() {
241         String filenamePattern = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "-collision-%d.log.zip";
242 
243         RollingFileAppender<Object> appender0 = new RollingFileAppender<Object>();
244         appender0.setName("FA0");
245         appender0.setContext(context);
246         appender0.setEncoder(new DummyEncoder<Object>());
247         TimeBasedRollingPolicy<Object> tbrp0 = new TimeBasedRollingPolicy<Object>();
248         tbrp0.setContext(context);
249         tbrp0.setFileNamePattern(filenamePattern);
250         tbrp0.setParent(appender0);
251         tbrp0.start();
252         appender0.setRollingPolicy(tbrp0);
253         appender0.start();
254         Assertions.assertTrue(appender0.isStarted());
255 
256         RollingFileAppender<Object> appender1 = new RollingFileAppender<Object>();
257         appender1.setName("FA1");
258         appender1.setFile("X");
259         appender1.setContext(context);
260         appender1.setEncoder(new DummyEncoder<Object>());
261         TimeBasedRollingPolicy<Object> tbrp1 = new TimeBasedRollingPolicy<Object>();
262         tbrp1.setContext(context);
263         tbrp1.setFileNamePattern(filenamePattern);
264         tbrp1.setParent(appender1);
265         tbrp1.start();
266         appender1.setRollingPolicy(tbrp1);
267         appender1.start();
268 
269         // StatusPrinter.print(context);
270 
271         Assertions.assertFalse(appender1.isStarted());
272         StatusChecker checker = new StatusChecker(context);
273         checker.assertContainsMatch(Status.ERROR, "'FileNamePattern' option has the same value");
274     }
275 
276     @Test
277     @DisplayName("Checks header and footer are written when the files are rolled")
278     public void testHeaderFooterWritten() throws IOException, InterruptedException {
279 
280         String folderPrefix = CoreTestConstants.OUTPUT_DIR_PREFIX+diff+"/";
281         String namePrefix = folderPrefix+"header-";
282         File folderFile = new File(folderPrefix);
283         FileUtil.createMissingParentDirectories(folderFile);
284 
285 
286         encoder.setFileHeader("HEADER");
287         encoder.setFileFooter("FOOTER");
288         rfa.setContext(context);
289         FixedWindowRollingPolicy fixedWindowRollingPolicy = new FixedWindowRollingPolicy();
290         fixedWindowRollingPolicy.setContext(context);
291         fixedWindowRollingPolicy.setParent(rfa);
292         fixedWindowRollingPolicy.setMaxIndex(3);
293         String fileNamePattern = namePrefix + "%i.log";
294         fixedWindowRollingPolicy.setFileNamePattern(fileNamePattern);
295         rfa.setRollingPolicy(fixedWindowRollingPolicy);
296         rfa.setFile(namePrefix+"0.log");
297         fixedWindowRollingPolicy.start();
298         rfa.setImmediateFlush(true);
299         SizeBasedTriggeringPolicy<Object> sbtp = new SizeBasedTriggeringPolicy<>();
300         sbtp.setMaxFileSize(new FileSize(10));
301         sbtp.setCheckIncrement(Duration.buildByMilliseconds(10));
302 
303         rfa.setTriggeringPolicy(sbtp);
304         rfa.getTriggeringPolicy().start();
305         rfa.start();
306 
307         for (int i = 0; i < 100; i++) {
308             rfa.doAppend("data" + i);
309             File file = new File(namePrefix + fixedWindowRollingPolicy.getMaxIndex() + ".log");
310             if (file.exists()) {
311                 break;
312             }
313             Thread.sleep(5);
314         }
315         rfa.stop();
316 
317         for (int i = 0; i < fixedWindowRollingPolicy.getMaxIndex(); i++) {
318             File file = new File(namePrefix + i + ".log");
319             Assertions.assertTrue(file.exists());
320             List<String> lines = Files.readAllLines(file.toPath());
321             Assertions.assertTrue(lines.size() > 2, "At least 2 lines per file are expected in " + file);
322             Assertions.assertEquals("HEADER", lines.get(0));
323             Assertions.assertEquals("FOOTER", lines.get(lines.size() - 1));
324             Assertions.assertEquals(1, Collections.frequency(lines, "HEADER"));
325             Assertions.assertEquals(1, Collections.frequency(lines, "FOOTER"));
326         }
327     }
328 
329 }