View Javadoc
1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2023, 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  
15  package ch.qos.logback.core.util;
16  
17  import ch.qos.logback.core.Context;
18  import ch.qos.logback.core.ContextBase;
19  import ch.qos.logback.core.FileAppender;
20  import ch.qos.logback.core.encoder.EchoEncoder;
21  import ch.qos.logback.core.status.testUtil.StatusChecker;
22  import ch.qos.logback.core.testUtil.CoreTestConstants;
23  import ch.qos.logback.core.testUtil.RandomUtil;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  
27  import java.io.File;
28  import java.io.IOException;
29  import java.nio.file.Files;
30  import java.nio.file.Paths;
31  import java.util.ArrayList;
32  import java.util.List;
33  import java.util.concurrent.CountDownLatch;
34  
35  import static org.junit.jupiter.api.Assertions.assertEquals;
36  import static org.junit.jupiter.api.Assertions.fail;
37  
38  public class PrudentModeTest {
39  
40      FileAppender<Object> fa = new FileAppender<Object>();
41      Context context = new ContextBase();
42  
43      StatusChecker statusChecker = new StatusChecker(context);
44      int diff = RandomUtil.getPositiveInt();
45  
46      String outputDirStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "prudentLockTest-" + diff + "/";
47      String logfileStr = outputDirStr + "output.log";
48  
49      private static final int THREAD_COUNT = 8;
50      private static final int LOOP_COUNT = 100/ THREAD_COUNT;
51  
52      @BeforeEach
53      public void beforeEach() {
54          File outputDir = new File(outputDirStr);
55          if (!outputDir.mkdirs()) {
56              fail("failed to create folder " + outputDir);
57          }
58  
59          fa.setContext(context);
60          fa.setName("FILE");
61          fa.setPrudent(true);
62          fa.setEncoder(new EchoEncoder<Object>());
63          fa.setFile(logfileStr);
64          fa.start();
65      }
66  
67      // see https://jira.qos.ch/browse/LOGBACK-1754
68      @Test
69      public void assertNoOverlappingFileLockException () throws IOException {
70          CountDownLatch latch = new CountDownLatch(1);
71          List<Thread> threads = new ArrayList<>(THREAD_COUNT);
72          for (int i = 0; i < THREAD_COUNT; i++) {
73              LoggerThread thread = new LoggerThread(latch, "message from thread " + i);
74              thread.start();
75              threads.add(thread);
76          }
77          latch.countDown();
78          int i = 0;
79          for (Thread thread : threads) {
80              try {
81                  thread.join();
82              } catch (InterruptedException e) {
83                  e.printStackTrace();
84              }
85          }
86          StatusPrinter.print(context);
87          statusChecker.assertIsWarningOrErrorFree();
88  
89          fa.stop();
90  
91          File file = new File(logfileStr);
92          List<String> allLines = Files.readAllLines(file.toPath());
93          int actualLineCount = allLines.size();
94          assertEquals(LOOP_COUNT*THREAD_COUNT, actualLineCount, "unexpected line count "+actualLineCount);
95  
96      }
97  
98      class LoggerThread extends Thread {
99          private final CountDownLatch latch;
100         private final String message;
101 
102         LoggerThread(CountDownLatch latch, String message) {
103             setDaemon(false);
104             this.latch = latch;
105             this.message = message;
106         }
107 
108         @Override
109         public void run() {
110             try {
111                 latch.await();
112                 for (int i = 0; i < LOOP_COUNT; i++) {
113                     if ((i & 0x08) == 0) {
114                         // yield to spice it up
115                         Thread.yield();
116                     }
117                     PrudentModeTest.this.fa.doAppend(message + " i=" + i);
118                 }
119             } catch (InterruptedException ex) {
120                 Thread.currentThread().interrupt();
121             }
122         }
123 
124         void delay(long millis) {
125             try {
126                 Thread.sleep(millis);
127             } catch (InterruptedException e) {
128                 throw new RuntimeException(e);
129             }
130         }
131     }
132 }
133