Skip to content

Commit 13d1369

Browse files
authored
Merge pull request #153 from hankolerd/master
Resolves #152 - Adds option to redirect program output of exec:exec to the maven logger.
2 parents dbe2c73 + 7935062 commit 13d1369

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed

src/main/java/org/codehaus/mojo/exec/ExecMojo.java

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,82 @@ public class ExecMojo
148148
@Parameter( property = "exec.outputFile" )
149149
private File outputFile;
150150

151+
/**
152+
* When enabled, program standard and error output will be redirected to the
153+
* Maven logger as <i>Info</i> and <i>Error</i> level logs, respectively. If not enabled the
154+
* traditional behavior of program output being directed to standard System.out
155+
* and System.err is used.<br>
156+
* <br>
157+
* NOTE: When enabled, to log the program standard out as Maven <i>Debug</i> level instead of
158+
* <i>Info</i> level use {@code exec.quietLogs=true}. <br>
159+
* <br>
160+
* This option can be extremely helpful when combined with multithreaded builds
161+
* for two reasons:<br>
162+
* <ul>
163+
* <li>Program output is suffixed with the owning thread name, making it easier
164+
* to trace execution of a specific projects build thread.</li>
165+
* <li>Program output will not get jumbled with other maven log messages.</li>
166+
* </ul>
167+
*
168+
* For Example, if using {@code exec:exec} to run a script to echo a count from
169+
* 1 to 100 as:
170+
*
171+
* <pre>
172+
* for i in {1..100}
173+
* do
174+
* echo "${project.artifactId} - $i"
175+
* done
176+
* </pre>
177+
*
178+
* When this script is run multi-threaded on two modules, {@code module1} and
179+
* {@code module2}, you might get output such as:
180+
*
181+
* <pre>
182+
* [BuilderThread 1] [INFO] --- exec-maven-plugin:1.6.0:exec (test) @ module1 ---
183+
* [BuilderThread 2] [INFO] --- exec-maven-plugin:1.6.0:exec (test) @ module2 ---
184+
* ...
185+
* module2 - 98
186+
* modu
187+
* module1 - 97
188+
* module1 -
189+
* le2 - 9899
190+
* ...
191+
* </pre>
192+
*
193+
* With this flag enabled, the output will instead come something similar to:
194+
*
195+
* <pre>
196+
* ...
197+
* [Exec Stream Pumper] [INFO] [BuilderThread 2] module2 - 98
198+
* [Exec Stream Pumper] [INFO] [BuilderThread 1] module1 - 97
199+
* [Exec Stream Pumper] [INFO] [BuilderThread 1] module1 - 98
200+
* [Exec Stream Pumper] [INFO] [BuilderThread 2] module2 - 99
201+
* ...
202+
* </pre>
203+
*
204+
* NOTE 1: To show the thread in the Maven log, configure the Maven
205+
* installations <i>conf/logging/simplelogger.properties</i> option:
206+
* {@code org.slf4j.simpleLogger.showThreadName=true}<br>
207+
*
208+
* NOTE 2: This option is ignored when {@code exec.outputFile} is specified.
209+
*
210+
* @since 3.0.0
211+
* @see java.lang.System#err
212+
* @see java.lang.System#in
213+
*/
214+
@Parameter( property = "exec.useMavenLogger", defaultValue = "false" )
215+
private boolean useMavenLogger;
216+
217+
/**
218+
* When combined with {@code exec.useMavenLogger=true}, prints all executed
219+
* program output at debug level instead of the default info level to the Maven
220+
* logger.
221+
*
222+
* @since 3.0.0
223+
*/
224+
@Parameter( property = "exec.quietLogs", defaultValue = "false" )
225+
private boolean quietLogs;
226+
151227
/**
152228
* <p>
153229
* A list of arguments passed to the {@code executable}, which should be of type <code>&lt;argument&gt;</code> or
@@ -340,6 +416,42 @@ else if ( !StringUtils.isEmpty( argsProp ) )
340416
IOUtil.close( outputStream );
341417
}
342418
}
419+
else if (useMavenLogger)
420+
{
421+
getLog().debug("Will redirect program output to Maven logger");
422+
final String parentThreadName = Thread.currentThread().getName();
423+
final String logSuffix = "[" + parentThreadName + "] ";
424+
Invokable<String> mavenOutRedirect = new Invokable<String>()
425+
{
426+
427+
@Override
428+
public void accept(String logMessage)
429+
{
430+
if (quietLogs)
431+
{
432+
getLog().debug(logSuffix + logMessage);
433+
}
434+
else
435+
{
436+
getLog().info(logSuffix + logMessage);
437+
}
438+
}
439+
};
440+
Invokable<String> mavenErrRedirect = new Invokable<String>()
441+
{
442+
443+
@Override
444+
public void accept(String logMessage)
445+
{
446+
getLog().error(logSuffix + logMessage);
447+
}
448+
};
449+
450+
try (OutputStream out = new LineRedirectOutputStream(mavenOutRedirect);
451+
OutputStream err = new LineRedirectOutputStream(mavenErrRedirect)) {
452+
resultCode = executeCommandLine(exec, commandLine, enviro, out, err);
453+
}
454+
}
343455
else
344456
{
345457
resultCode = executeCommandLine( exec, commandLine, enviro, System.out, System.err );
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.codehaus.mojo.exec;
2+
3+
/*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
/**
23+
* A simple Java 7 pseudo-class based on the Java 8 {@code Consumer} class. Can and should be
24+
* deleted once this project moves to a minimum execution environment of Java 8+.
25+
*
26+
* @param <T> - The type of object that will be acted upon.
27+
* @since 3.0.0
28+
*/
29+
interface Invokable<T> {
30+
31+
/**
32+
* Takes some object and acts upon it.
33+
*
34+
* @param object - The object that will be taken
35+
*/
36+
void accept(T object);
37+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.codehaus.mojo.exec;
2+
3+
/*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import java.io.OutputStream;
23+
24+
/**
25+
* An output stream that captures one line of output at a time, and then
26+
* redirects that line to some {@link Invokable} to act upon as it pleases. This
27+
* class is not thread safe and expects to have only one active writer consuming
28+
* it at any given time.
29+
*
30+
* @since 3.0.0
31+
*/
32+
class LineRedirectOutputStream extends OutputStream {
33+
34+
private StringBuilder currentLine = new StringBuilder();
35+
private final Invokable<String> linePrinter;
36+
37+
public LineRedirectOutputStream(Invokable<String> linePrinter) {
38+
this.linePrinter = linePrinter;
39+
}
40+
41+
@Override
42+
public void write(final int b) {
43+
if ((char) b == '\n') {
44+
printAndReset();
45+
return;
46+
}
47+
currentLine.append((char) b);
48+
}
49+
50+
@Override
51+
public void flush() {
52+
if (currentLine.length() > 0) {
53+
printAndReset();
54+
}
55+
}
56+
57+
private void printAndReset() {
58+
linePrinter.accept(currentLine.toString());
59+
currentLine = new StringBuilder();
60+
}
61+
}

0 commit comments

Comments
 (0)