Skip to content

Commit 796221a

Browse files
Provide separate logs for each execution for rerunning jobs
Many assertions simply search for a specific string in build.log, and failed assertions are written to that log as well. If we keep appending every run’s output to a single file, assertions in subsequent executions may match strings left over from previous runs, leading to false positives (i.e., reporting results that don’t belong to the current execution).
1 parent 4e77523 commit 796221a

6 files changed

Lines changed: 85 additions & 78 deletions

File tree

src/it/fail-build-streamLogsOnFailures/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ under the License.
4747
<pomInclude>*/pom.xml</pomInclude>
4848
</pomIncludes>
4949
<streamLogsOnFailures>true</streamLogsOnFailures>
50+
<rerunFailingTestsCount>1</rerunFailingTestsCount>
5051
</configuration>
5152
<executions>
5253
<execution>

src/it/fail-build-streamLogsOnFailures/verify.groovy

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,19 @@
1818
*/
1919

2020
def FS = File.separator
21-
def buildLogOfProject = new File( basedir, 'target/it/project/build.log' ).text
21+
def buildLogOfProject1 = new File(basedir, 'target/it/project/build.log.1').text
22+
def buildLogOfProject = new File(basedir, 'target/it/project/build.log').text
2223

23-
def buildLog = new File( basedir, 'build.log' ).text
24+
def buildLog = new File(basedir, 'build.log').text
2425

25-
assert buildLog.contains( '*** begin build.log for: project' + FS + 'pom.xml ***' )
26-
assert buildLog.contains( buildLogOfProject )
27-
assert buildLog.contains( '*** end build.log for: project' + FS + 'pom.xml ***' )
26+
assert buildLog.contains('*** begin build.log for: project' + FS + 'pom.xml ***')
27+
assert buildLog.contains('*** build.log for execution: 1 ***')
28+
assert buildLog.contains(buildLogOfProject1)
29+
assert buildLog.contains('*** build.log for execution: 2 ***')
30+
assert buildLog.contains(buildLogOfProject)
31+
assert buildLog.contains('*** end build.log for: project' + FS + 'pom.xml ***')
2832

29-
assert buildLog.contains( 'ERROR] Failed to execute goal org.apache.maven.plugins:maven-invoker-plugin:' + projectVersion + ':run' )
33+
// the build was re-run so the error is logged twice, once for each run
34+
assert buildLog.count("[FATAL] 'modelVersion' of '99.0.0'") == 2
35+
36+
assert buildLog.contains('ERROR] Failed to execute goal org.apache.maven.plugins:maven-invoker-plugin:' + projectVersion + ':run')

src/it/rerun-build/verify.groovy

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import java.nio.file.Files
2020
*/
2121

2222

23-
assert new File(basedir, 'target/it/setup/touch.txt' ).exists()
24-
assert new File(basedir, 'target/it/project/touch.txt' ).exists()
23+
assert new File(basedir, 'target/it/setup/touch.txt').exists()
24+
assert new File(basedir, 'target/it/project/touch.txt').exists()
2525

2626
def buildLog = new File(basedir, 'build.log').text
2727

@@ -33,12 +33,14 @@ assert buildLog.count("setup${fs}pom.xml .................................... SU
3333
assert buildLog.count("project${fs}pom.xml .................................. FAILED") == 1
3434
assert buildLog.count("project${fs}pom.xml .................................. SUCCESS") == 1
3535

36+
def setupBuildLog1 = new File(basedir, 'target/it/setup/build.log.1').text
37+
assert setupBuildLog1.count("[INFO] BUILD SUCCESS") == 1
38+
3639
def setupBuildLog = new File(basedir, 'target/it/setup/build.log').text
40+
assert setupBuildLog.count("[INFO] BUILD SUCCESS") == 1
3741

38-
// The project should be built twice, once for the first run and once for the rerun.
39-
assert setupBuildLog.count("[INFO] BUILD SUCCESS") == 2
42+
def projectBuildLog1 = new File(basedir, 'target/it/project/build.log.1').text
43+
assert projectBuildLog1.count("[INFO] BUILD SUCCESS") == 1
4044

4145
def projectBuildLog = new File(basedir, 'target/it/project/build.log').text
42-
43-
// The project should be built twice, once for the first run and once for the rerun.
44-
assert projectBuildLog.count("[INFO] BUILD SUCCESS") == 2
46+
assert projectBuildLog.count("[INFO] BUILD SUCCESS") == 1

src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.nio.file.Files;
3333
import java.nio.file.Path;
3434
import java.nio.file.Paths;
35+
import java.nio.file.StandardCopyOption;
3536
import java.text.MessageFormat;
3637
import java.util.ArrayList;
3738
import java.util.Arrays;
@@ -2097,15 +2098,22 @@ private FileLogger setupBuildLogFile(File basedir, int executionCount) throws Mo
20972098
}
20982099

20992100
Log streamLogger = streamLogs ? getLog() : null;
2100-
File logFile = projectLogDirectory.resolve("build.log").toFile();
2101+
Path logPath = projectLogDirectory.resolve("build.log");
2102+
21012103
try {
2102-
if (executionCount > 1) {
2103-
logger = new FileLoggerAppender(logFile, streamLogger);
2104-
getLog().debug("Append to build log: " + logFile);
2105-
} else {
2106-
logger = new FileLogger(logFile, streamLogger);
2107-
getLog().debug("New build log initialized: " + logFile);
2104+
if (executionCount > 1 && Files.exists(logPath)) {
2105+
// next execution, rename log file from build.log to build-<executionCount -1 >.log
2106+
// we preserve log from previous execution, and start with new log file for current execution
2107+
// each execution should have new log file as many assertions check string presence in log file, a
2108+
// nd if we keep appending to same log file, it will be hard to determine which log entry belongs to
2109+
// which execution
2110+
Path logFileBackup =
2111+
logPath.getParent().resolve(logPath.getFileName().toString() + "." + (executionCount - 1));
2112+
getLog().debug("Renaming existing log file " + logPath + " to " + logFileBackup);
2113+
Files.move(logPath, logFileBackup, StandardCopyOption.REPLACE_EXISTING);
21082114
}
2115+
logger = new FileLogger(logPath.toFile(), streamLogger);
2116+
getLog().debug("New build log initialized: " + logPath);
21092117
} catch (IOException e) {
21102118
throw new MojoExecutionException("Error initializing build logfile in: " + projectLogDirectory, e);
21112119
}

src/main/java/org/apache/maven/plugins/invoker/FileLoggerAppender.java

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.IOException;
2323
import java.io.Reader;
2424
import java.nio.file.Files;
25+
import java.nio.file.Path;
2526
import java.util.ArrayList;
2627
import java.util.List;
2728

@@ -201,21 +202,51 @@ public void logFailedBuildLog(Log logger, boolean ignoreFailures) throws MojoFai
201202
jobToLogs.addAll(errorJobs);
202203

203204
for (BuildJob buildJob : jobToLogs) {
204-
File buildLogFile = buildJob.getBuildlog() != null ? new File(buildJob.getBuildlog()) : null;
205-
if (buildLogFile != null && buildLogFile.exists()) {
205+
Path buildLogPath = buildJob.getBuildlog() != null ? new File(buildJob.getBuildlog()).toPath() : null;
206+
if (buildLogPath != null && Files.exists(buildLogPath)) {
206207
try {
207208
// prepare message with build.log in one string to omit begin [ERROR], [WARN]
208209
// so whole log will be displayed without decoration
209210
StringBuilder buildLogMessage = new StringBuilder();
210211
buildLogMessage.append(System.lineSeparator());
211212
buildLogMessage.append(System.lineSeparator());
212-
buildLogMessage.append("*** begin build.log for: " + buildJob.getProject() + " ***");
213-
buildLogMessage.append(System.lineSeparator());
214-
try (Reader buildLogReader = Files.newBufferedReader(buildLogFile.toPath())) {
215-
buildLogMessage.append(IOUtil.toString(buildLogReader));
213+
buildLogMessage
214+
.append("*** begin build.log for: ")
215+
.append(buildJob.getProject())
216+
.append(" ***")
217+
.append(System.lineSeparator());
218+
219+
if (buildJob.getExecutionCount() > 1) {
220+
// try to read build log from each execution
221+
for (int executionCount = 1;
222+
executionCount <= buildJob.getExecutionCount() - 1;
223+
executionCount++) {
224+
// log file name is hardcoded in whole project
225+
buildLogMessage
226+
.append("*** build.log for execution: ")
227+
.append(executionCount)
228+
.append(" ***")
229+
.append(System.lineSeparator());
230+
Path path = buildLogPath
231+
.getParent()
232+
.resolve(buildLogPath.getFileName().toString() + "." + executionCount);
233+
appendLogToMessage(path, buildLogMessage);
234+
}
235+
236+
buildLogMessage
237+
.append("*** build.log for execution: ")
238+
.append(buildJob.getExecutionCount())
239+
.append(" ***")
240+
.append(System.lineSeparator());
216241
}
217-
buildLogMessage.append("*** end build.log for: " + buildJob.getProject() + " ***");
218-
buildLogMessage.append(System.lineSeparator());
242+
243+
appendLogToMessage(buildLogPath, buildLogMessage);
244+
245+
buildLogMessage
246+
.append("*** end build.log for: ")
247+
.append(buildJob.getProject())
248+
.append(" ***")
249+
.append(System.lineSeparator());
219250

220251
logWithLevel(logger, ignoreFailures, SEPARATOR);
221252
logWithLevel(logger, ignoreFailures, buildLogMessage.toString());
@@ -229,6 +260,14 @@ public void logFailedBuildLog(Log logger, boolean ignoreFailures) throws MojoFai
229260
}
230261
}
231262

263+
private void appendLogToMessage(Path logPath, StringBuilder buildLogMessage) throws IOException {
264+
if (logPath != null && Files.exists(logPath)) {
265+
try (Reader buildLogReader = Files.newBufferedReader(logPath)) {
266+
buildLogMessage.append(IOUtil.toString(buildLogReader));
267+
}
268+
}
269+
}
270+
232271
/**
233272
* Handles the build failures in this session.
234273
*

0 commit comments

Comments
 (0)