1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.core;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.nio.channels.FileChannel;
19
20 import org.junit.jupiter.api.Assertions;
21 import org.junit.jupiter.api.BeforeEach;
22 import org.junit.jupiter.api.Disabled;
23 import org.junit.jupiter.api.Test;
24
25 import ch.qos.logback.core.contention.RunnableWithCounterAndDone;
26 import ch.qos.logback.core.encoder.EchoEncoder;
27 import ch.qos.logback.core.recovery.RecoveryCoordinator;
28 import ch.qos.logback.core.recovery.RecoveryListener;
29 import ch.qos.logback.core.recovery.ResilientFileOutputStream;
30 import ch.qos.logback.core.status.OnConsoleStatusListener;
31 import ch.qos.logback.core.testUtil.CoreTestConstants;
32 import ch.qos.logback.core.testUtil.RandomUtil;
33 import ch.qos.logback.core.util.ResilienceUtil;
34
35 public class FileAppenderResilienceTest implements RecoveryListener {
36
37 FileAppender<Object> fa = new FileAppender<Object>();
38
39 ResilientFileOutputStream resilientFOS;
40
41 Context context = new ContextBase();
42 int diff = RandomUtil.getPositiveInt();
43 String outputDirStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "resilience-" + diff + "/";
44
45
46
47 String logfileStr = outputDirStr + "output.log";
48
49 boolean failedState = false;
50
51 int recoveryCounter = 0;
52 int failureCounter = 0;
53
54
55 @BeforeEach
56 public void setUp() throws InterruptedException {
57
58 context.getStatusManager().add(new OnConsoleStatusListener());
59
60 File outputDir = new File(outputDirStr);
61 outputDir.mkdirs();
62
63 fa.setContext(context);
64 fa.setName("FILE");
65 fa.setEncoder(new EchoEncoder<Object>());
66 fa.setFile(logfileStr);
67 fa.start();
68 resilientFOS = (ResilientFileOutputStream) fa.getOutputStream();
69 resilientFOS.addRecoveryListener(this);
70
71 }
72
73 @Test
74 @Disabled
75 public void manual() throws InterruptedException, IOException {
76 Runner runner = new Runner(fa);
77 Thread t = new Thread(runner);
78 t.start();
79
80 while (true) {
81 Thread.sleep(110);
82 }
83 }
84
85 @Test
86 public void smoke() throws InterruptedException, IOException {
87 Runner runner = new Runner(fa);
88 Thread t = new Thread(runner);
89 t.start();
90
91 double delayCoefficient = 2.0;
92 for (int i = 0; i < 5; i++) {
93 Thread.sleep((int) (RecoveryCoordinator.BACKOFF_COEFFICIENT_MIN * delayCoefficient));
94 closeLogFileOnPurpose();
95 }
96 runner.setDone(true);
97 t.join();
98
99 double bestCaseSuccessRatio = 1 / delayCoefficient;
100
101 double lossinessFactor = 0.35;
102 double resilianceFactor = (1 - lossinessFactor);
103
104 Assertions.assertTrue(recoveryCounter > 0, "at least one recovery should have occured");
105 Assertions.assertTrue(failureCounter > 0, "at least one failure should have occured");
106
107 System.out.println("recoveryCounter=" + recoveryCounter);
108 System.out.println("failureCounter=" + failureCounter);
109
110
111
112 String errmsg0 = "failureCounter="+failureCounter+" must be greater or equal to recoveryCounter="+recoveryCounter;
113 Assertions.assertTrue(failureCounter >= recoveryCounter, errmsg0);
114
115 String errmsg1 = "Difference between failureCounter="+failureCounter+" and recoveryCounter="+recoveryCounter+" should not exceeed 1";
116 Assertions.assertTrue(failureCounter - recoveryCounter <= 1, errmsg1);
117
118
119
120 int actuallyWritten = ResilienceUtil.countLines(logfileStr, "^hello (\\d{1,5})$");
121 long exptectedWrites = runner.getCounter()-recoveryCounter;
122 Assertions.assertEquals(exptectedWrites, actuallyWritten);
123 }
124
125 private void closeLogFileOnPurpose() throws IOException {
126 ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) fa.getOutputStream();
127 FileChannel fileChannel = resilientFOS.getChannel();
128 fileChannel.close();
129 }
130
131 @Override
132 public void newFailure(IOException e) {
133 failedState = true;
134 failureCounter++;
135
136 }
137
138 @Override
139 public void recoveryOccured() {
140 failedState = false;
141 recoveryCounter++;
142 }
143
144 class Runner extends RunnableWithCounterAndDone {
145 FileAppender<Object> fa;
146
147 Runner(FileAppender<Object> fa) {
148 this.fa = fa;
149 }
150
151 public void run() {
152 while (!isDone()) {
153 fa.doAppend("hello " + counter);
154 if(!FileAppenderResilienceTest.this.failedState) {
155 counter++;
156 }
157 if (counter % 128 == 0) {
158 try {
159 Thread.sleep(10);
160 } catch (InterruptedException e) {
161 }
162 }
163 }
164 }
165
166 }
167 }
168