Skip to content

Commit 0cc253f

Browse files
authored
Release 4.4 (#144)
2 parents 9742b46 + 636aaea commit 0cc253f

File tree

6 files changed

+224
-12
lines changed

6 files changed

+224
-12
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<groupId>fr.insalyon.creatis</groupId>
88
<artifactId>gasw</artifactId>
99
<packaging>jar</packaging>
10-
<version>4.3</version>
10+
<version>4.4</version>
1111
<name>GASW</name>
1212

1313
<properties>

src/main/java/fr/insalyon/creatis/gasw/execution/GaswOutputParser.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ protected int parseStdOut(File stdOut) {
177177
boolean isAppExec = false;
178178
boolean isInputDownload = false;
179179
boolean isResultUpload = false;
180-
boolean isAfterExec = false;
181180
String lfcHost = "";
182181

183182
try {
@@ -190,8 +189,7 @@ protected int parseStdOut(File stdOut) {
190189
if (line.contains("<application_execution>")) {
191190
isAppExec = true;
192191
} else if (line.contains("</application_execution>")) {
193-
isAppExec = false;
194-
isAfterExec = true;
192+
isAppExec = false;;
195193
} else if (isAppExec) {
196194
appStdOutWriter.write(line + "\n");
197195
appStdOutBuf.append(line).append("\n");
@@ -214,7 +212,7 @@ protected int parseStdOut(File stdOut) {
214212
int uploadTime = Integer.parseInt(lineSplitted[lineSplitted.length - 2]);
215213
job.setEnd(addDate(job.getUpload(), Calendar.SECOND, uploadTime));
216214

217-
} else if (line.contains("Exiting with return value") && isAfterExec) {
215+
} else if (line.contains("Exiting with return value")) {
218216
String[] errmsg = line.split("\\s+");
219217
exitCode = Integer.parseInt(errmsg[errmsg.length - 1]);
220218
job.setExitCode(exitCode);

src/main/resources/logback.xml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
11
<configuration debug="false" scan="true" scanPeriod="60 seconds" packagingData="true">
22
<!-- main log file-->
3-
<appender name="GASW_LOG" class="ch.qos.logback.core.FileAppender">
3+
<appender name="GASW_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
44
<file>workflow.out</file>
5+
6+
<!-- size limited and minimum rolling : 1 file -->
7+
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
8+
<maxFileSize>100MB</maxFileSize>
9+
</triggeringPolicy>
10+
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
11+
<fileNamePattern>workflow-%i.out</fileNamePattern>
12+
<minIndex>1</minIndex>
13+
<maxIndex>1</maxIndex>
14+
</rollingPolicy>
15+
516
<encoder>
617
<pattern>%d | %5p | %t | %c{1}:%L - %m%n %ex{2}</pattern>
718
</encoder>
819
</appender>
920

1021
<!-- error log file-->
11-
<appender name="GASW_ERROR" class="ch.qos.logback.core.FileAppender">
22+
<appender name="GASW_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
1223
<file>workflow.err</file>
24+
25+
<!-- size limited and minimum rolling : 1 file -->
26+
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
27+
<maxFileSize>100MB</maxFileSize>
28+
</triggeringPolicy>
29+
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
30+
<fileNamePattern>workflow-%i.err</fileNamePattern>
31+
<minIndex>1</minIndex>
32+
<maxIndex>1</maxIndex>
33+
</rollingPolicy>
34+
1335
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
1436
<level>WARN</level>
1537
</filter>
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package fr.insalyon.creatis.gasw;
2+
3+
import ch.qos.logback.classic.LoggerContext;
4+
import ch.qos.logback.classic.util.ContextInitializer;
5+
import ch.qos.logback.core.joran.spi.JoranException;
6+
import org.junit.jupiter.api.AfterEach;
7+
import org.junit.jupiter.api.Assertions;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.Test;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
import java.io.IOException;
14+
import java.net.URISyntaxException;
15+
import java.net.URL;
16+
import java.nio.file.Files;
17+
import java.nio.file.Path;
18+
import java.nio.file.Paths;
19+
import java.util.Arrays;
20+
import java.util.List;
21+
import java.util.stream.Collectors;
22+
import java.util.stream.Stream;
23+
24+
/**
25+
* We need to limit the log size to avoid them to make a full on disk when there is an infinite loop with an error
26+
* (this happened)
27+
* At first, I wanted to configure logback to keep only the beginning of the logs and stop once a limit is attained.
28+
* This does not seem to be possible (easily at least)
29+
* So I did a classic config, with a simple rolling (only 1 rolled file) and a size limit.
30+
* So logback will a log file workflow.out (and workflow.err), and move it to workflow-1.out (and workflow-1.err) once
31+
* it gets bigger than the limit (deleting the previous workflow-1.out/err if they existed).
32+
*
33+
* We test that here by :
34+
* - using a logback-test.xml config file only used in test
35+
* - ensure that this behaves as expected with a limit of 1KB
36+
* - then we ensure that the logback.xml config file used in production is almost the same as logback-test.xml except
37+
* the small stuff necessary for the tests (size of 1KB instead of 100MB, and check every millisecond)
38+
*
39+
* Also, to test a clean context, we delete the logs files before the test.
40+
* We also have to reload logback after that because logback won't recreate the manually deleted log files.
41+
*
42+
*/
43+
public class GaswLoggerTest {
44+
45+
@Test
46+
public void verifyProdConfigIsTheSameAsTheTestOne() throws IOException, URISyntaxException {
47+
// compare logback-test.xml (used in the next test) and logback.xml (used in prod)
48+
// only small test stuff must change
49+
URL prodConfigURL = getClass().getResource("/logback.xml");
50+
URL testConfigURL = getClass().getResource("/logback-test.xml");
51+
List<String> prodConfig = Files.readAllLines(Paths.get(prodConfigURL.toURI()));
52+
List<String> testConfig = Files.readAllLines(Paths.get(testConfigURL.toURI()));
53+
54+
// in test : remove the checkIncrement lines, flagged with a suffix
55+
testConfig.removeIf(line -> line.trim().endsWith("<!-- only for test -->"));
56+
57+
// also change and assert the max size is the expected one
58+
// assert the size is only configured twice (stdout and stderr)
59+
Assertions.assertEquals(2, testConfig.stream().filter(line -> line.trim().startsWith("<maxFileSize>")).count());
60+
testConfig = testConfig.stream().map(line -> {
61+
if ( ! line.trim().startsWith("<maxFileSize>")) {
62+
return line;
63+
}
64+
return line.replace("1KB", "100MB");
65+
}).collect(Collectors.toList());
66+
67+
Assertions.assertIterableEquals(testConfig, prodConfig);
68+
}
69+
70+
@Test
71+
public void testLogFileSizeLimit() throws IOException, InterruptedException {
72+
Logger logger = LoggerFactory.getLogger(Gasw.class);
73+
// 100 log lines should take around 10KB
74+
for (int i = 0; i<100; i++) {
75+
// wait a little because logback has a timeout and do not verify the size if the logs are too close
76+
// in logback-test.xml, logcback si configured to test every millisecond
77+
Thread.sleep(2);
78+
logger.error(i + " / This test line should take around 100B");
79+
}
80+
// verify
81+
assertLogFiles();
82+
}
83+
84+
@BeforeEach
85+
@AfterEach
86+
public void cleanLogFiles() throws IOException, JoranException {
87+
try (Stream<Path> stream = Files.list(Paths.get(""))) {
88+
stream
89+
.filter(file -> ! Files.isDirectory(file))
90+
.filter(file -> file.getFileName().toString().startsWith("workflow"))
91+
.forEach(f -> {
92+
System.out.println("deleting : " + f.getFileName());
93+
f.toFile().delete();
94+
});
95+
}
96+
/*
97+
If we do not reload, as we delete the log file after logback has started (in other tests),
98+
logback will not re-create them.
99+
*/
100+
System.out.println("reloading logback");
101+
reloadLogback();
102+
}
103+
104+
public void reloadLogback() throws JoranException {
105+
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
106+
context.reset();
107+
ContextInitializer initializer = new ContextInitializer(context);
108+
initializer.autoConfig();
109+
}
110+
111+
public void assertLogFiles() throws IOException {
112+
List<Path> logFiles;
113+
try (Stream<Path> stream = Files.list(Paths.get(""))) {
114+
logFiles = stream
115+
.filter(file -> ! Files.isDirectory(file))
116+
.filter(file -> file.getFileName().toString().startsWith("workflow"))
117+
.collect(Collectors.toList());
118+
119+
}
120+
// we should get only 1 log file and 1 roll-over for .out and .err
121+
List<String> authorizedNames = Arrays.asList("workflow.out", "workflow-1.out", "workflow.err", "workflow-1.err");
122+
for (Path logFile : logFiles) {
123+
Assertions.assertTrue(
124+
authorizedNames.contains(logFile.getFileName().toString()),
125+
"a log file is not workflow.out or workflow.err : " + logFile);
126+
}
127+
// rollover at 1KB. it must be 1 line bigger than 1KB, so not bigger than 1300 bytes with a margin
128+
for (Path logFile : logFiles) {
129+
Assertions.assertTrue(
130+
Files.size(logFile) < 1300,
131+
"a log file is too much bigger than 1KB : " + logFile
132+
+ " / size " + Files.size(logFile));
133+
}
134+
// verify logs are done
135+
for (String logFileName : authorizedNames) {
136+
Path logFile = Paths.get(logFileName);
137+
Assertions.assertTrue(Files.exists(logFile), "Missing log file " + logFile);
138+
139+
// there could be only 1 line if rolling has just happened
140+
List<String> logLines = Files.readAllLines(logFile);
141+
Assertions.assertTrue(logLines.size() > 0, "a log file is too small : " + logFileName);
142+
Assertions.assertTrue(
143+
logLines.get(0).contains("This test line should take around 100B"),
144+
"a log file does not contain the test log " + logFileName);
145+
}
146+
}
147+
}
148+

src/test/resources/log4j.properties

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<configuration debug="false" scan="true" scanPeriod="60 seconds" packagingData="true">
2+
<!-- main log file-->
3+
<appender name="GASW_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
4+
<file>workflow.out</file>
5+
6+
<!-- size limited and minimum rolling : 1 file -->
7+
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
8+
<checkIncrement>1 millisecond</checkIncrement> <!-- only for test -->
9+
<maxFileSize>1KB</maxFileSize>
10+
</triggeringPolicy>
11+
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
12+
<fileNamePattern>workflow-%i.out</fileNamePattern>
13+
<minIndex>1</minIndex>
14+
<maxIndex>1</maxIndex>
15+
</rollingPolicy>
16+
17+
<encoder>
18+
<pattern>%d | %5p | %t | %c{1}:%L - %m%n %ex{2}</pattern>
19+
</encoder>
20+
</appender>
21+
22+
<!-- error log file-->
23+
<appender name="GASW_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
24+
<file>workflow.err</file>
25+
26+
<!-- size limited and minimum rolling : 1 file -->
27+
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
28+
<checkIncrement>1 millisecond</checkIncrement> <!-- only for test -->
29+
<maxFileSize>1KB</maxFileSize>
30+
</triggeringPolicy>
31+
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
32+
<fileNamePattern>workflow-%i.err</fileNamePattern>
33+
<minIndex>1</minIndex>
34+
<maxIndex>1</maxIndex>
35+
</rollingPolicy>
36+
37+
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
38+
<level>WARN</level>
39+
</filter>
40+
<encoder>
41+
<pattern>%d | %5p | %t | %c{1}:%M:%L - %m%n %ex</pattern>
42+
</encoder>
43+
</appender>
44+
45+
<root level="INFO">
46+
<appender-ref ref="GASW_LOG"/>
47+
<appender-ref ref="GASW_ERROR"/>
48+
</root>
49+
</configuration>

0 commit comments

Comments
 (0)