mirror of https://github.com/apache/maven.git
[MNG-8309] Improve log infrastructure (first step toward multi-threading log view support) (#1792)
Move logging infrastructure to support multi threaded output from mvnd to maven. Refactor a bit the terminal/log creation done by Cling.
This commit is contained in:
parent
d742fd624c
commit
740100b50c
|
@ -99,7 +99,7 @@ under the License.
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-slf4j-provider</artifactId>
|
<artifactId>maven-logging</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jline</groupId>
|
<groupId>org.jline</groupId>
|
||||||
|
|
|
@ -166,6 +166,14 @@ public interface Options {
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Optional<String> logFile();
|
Optional<String> logFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether raw streams should be logged.
|
||||||
|
*
|
||||||
|
* @return a boolean indicating whether raw streams should be logged
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Optional<Boolean> rawStreams();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color setting for console output.
|
* Returns the color setting for console output.
|
||||||
*
|
*
|
||||||
|
|
|
@ -175,7 +175,7 @@ under the License.
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
<artifactId>maven-logging</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -181,6 +181,14 @@ public abstract class CommonsCliOptions implements Options {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Boolean> rawStreams() {
|
||||||
|
if (commandLine.hasOption(CLIManager.RAW_STREAMS)) {
|
||||||
|
return Optional.of(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> color() {
|
public Optional<String> color() {
|
||||||
if (commandLine.hasOption(CLIManager.COLOR)) {
|
if (commandLine.hasOption(CLIManager.COLOR)) {
|
||||||
|
@ -262,6 +270,7 @@ public abstract class CommonsCliOptions implements Options {
|
||||||
|
|
||||||
public static final String ALTERNATE_INSTALLATION_TOOLCHAINS = "it";
|
public static final String ALTERNATE_INSTALLATION_TOOLCHAINS = "it";
|
||||||
public static final String LOG_FILE = "l";
|
public static final String LOG_FILE = "l";
|
||||||
|
public static final String RAW_STREAMS = "raw-streams";
|
||||||
public static final String COLOR = "color";
|
public static final String COLOR = "color";
|
||||||
public static final String HELP = "h";
|
public static final String HELP = "h";
|
||||||
|
|
||||||
|
@ -348,6 +357,10 @@ public abstract class CommonsCliOptions implements Options {
|
||||||
.hasArg()
|
.hasArg()
|
||||||
.desc("Log file where all build output will go (disables output color)")
|
.desc("Log file where all build output will go (disables output color)")
|
||||||
.build());
|
.build());
|
||||||
|
options.addOption(Option.builder()
|
||||||
|
.longOpt(RAW_STREAMS)
|
||||||
|
.desc("Do not decorate standard output and error streams")
|
||||||
|
.build());
|
||||||
options.addOption(Option.builder(SHOW_VERSION)
|
options.addOption(Option.builder(SHOW_VERSION)
|
||||||
.longOpt("show-version")
|
.longOpt("show-version")
|
||||||
.desc("Display version information WITHOUT stopping build")
|
.desc("Display version information WITHOUT stopping build")
|
||||||
|
|
|
@ -122,6 +122,11 @@ public abstract class LayeredOptions<O extends Options> implements Options {
|
||||||
return returnFirstPresentOrEmpty(Options::logFile);
|
return returnFirstPresentOrEmpty(Options::logFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Boolean> rawStreams() {
|
||||||
|
return returnFirstPresentOrEmpty(Options::rawStreams);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> color() {
|
public Optional<String> color() {
|
||||||
return returnFirstPresentOrEmpty(Options::color);
|
return returnFirstPresentOrEmpty(Options::color);
|
||||||
|
|
|
@ -20,13 +20,14 @@ package org.apache.maven.cling.invoker;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -38,6 +39,7 @@ import org.apache.maven.api.cli.InvokerRequest;
|
||||||
import org.apache.maven.api.cli.Logger;
|
import org.apache.maven.api.cli.Logger;
|
||||||
import org.apache.maven.api.cli.Options;
|
import org.apache.maven.api.cli.Options;
|
||||||
import org.apache.maven.api.services.Lookup;
|
import org.apache.maven.api.services.Lookup;
|
||||||
|
import org.apache.maven.api.services.MavenException;
|
||||||
import org.apache.maven.api.services.MessageBuilder;
|
import org.apache.maven.api.services.MessageBuilder;
|
||||||
import org.apache.maven.artifact.InvalidRepositoryException;
|
import org.apache.maven.artifact.InvalidRepositoryException;
|
||||||
import org.apache.maven.artifact.repository.ArtifactRepository;
|
import org.apache.maven.artifact.repository.ArtifactRepository;
|
||||||
|
@ -53,9 +55,13 @@ import org.apache.maven.cli.transfer.QuietMavenTransferListener;
|
||||||
import org.apache.maven.cli.transfer.SimplexTransferListener;
|
import org.apache.maven.cli.transfer.SimplexTransferListener;
|
||||||
import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
|
import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
|
||||||
import org.apache.maven.execution.MavenExecutionRequest;
|
import org.apache.maven.execution.MavenExecutionRequest;
|
||||||
|
import org.apache.maven.jline.FastTerminal;
|
||||||
import org.apache.maven.jline.MessageUtils;
|
import org.apache.maven.jline.MessageUtils;
|
||||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
import org.apache.maven.logging.BuildEventListener;
|
||||||
import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
|
import org.apache.maven.logging.LoggingOutputStream;
|
||||||
|
import org.apache.maven.logging.ProjectBuildLogAppender;
|
||||||
|
import org.apache.maven.logging.SimpleBuildEventListener;
|
||||||
|
import org.apache.maven.logging.api.LogLevelRecorder;
|
||||||
import org.apache.maven.settings.Mirror;
|
import org.apache.maven.settings.Mirror;
|
||||||
import org.apache.maven.settings.Profile;
|
import org.apache.maven.settings.Profile;
|
||||||
import org.apache.maven.settings.Proxy;
|
import org.apache.maven.settings.Proxy;
|
||||||
|
@ -68,9 +74,14 @@ import org.apache.maven.settings.building.SettingsBuilder;
|
||||||
import org.apache.maven.settings.building.SettingsBuildingRequest;
|
import org.apache.maven.settings.building.SettingsBuildingRequest;
|
||||||
import org.apache.maven.settings.building.SettingsBuildingResult;
|
import org.apache.maven.settings.building.SettingsBuildingResult;
|
||||||
import org.apache.maven.settings.building.SettingsProblem;
|
import org.apache.maven.settings.building.SettingsProblem;
|
||||||
|
import org.apache.maven.slf4j.MavenSimpleLogger;
|
||||||
import org.eclipse.aether.transfer.TransferListener;
|
import org.eclipse.aether.transfer.TransferListener;
|
||||||
|
import org.jline.jansi.AnsiConsole;
|
||||||
|
import org.jline.terminal.Terminal;
|
||||||
|
import org.jline.terminal.TerminalBuilder;
|
||||||
import org.slf4j.ILoggerFactory;
|
import org.slf4j.ILoggerFactory;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.slf4j.spi.LocationAwareLogger;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static org.apache.maven.cling.invoker.Utils.toFile;
|
import static org.apache.maven.cling.invoker.Utils.toFile;
|
||||||
|
@ -111,9 +122,6 @@ public abstract class LookupInvoker<
|
||||||
public final Function<String, Path> cwdResolver;
|
public final Function<String, Path> cwdResolver;
|
||||||
public final Function<String, Path> installationResolver;
|
public final Function<String, Path> installationResolver;
|
||||||
public final Function<String, Path> userResolver;
|
public final Function<String, Path> userResolver;
|
||||||
public final InputStream stdIn;
|
|
||||||
public final PrintWriter stdOut;
|
|
||||||
public final PrintWriter stdErr;
|
|
||||||
|
|
||||||
protected LookupInvokerContext(LookupInvoker<O, R, C> invoker, R invokerRequest) {
|
protected LookupInvokerContext(LookupInvoker<O, R, C> invoker, R invokerRequest) {
|
||||||
this.invoker = invoker;
|
this.invoker = invoker;
|
||||||
|
@ -127,9 +135,6 @@ public abstract class LookupInvoker<
|
||||||
.toAbsolutePath();
|
.toAbsolutePath();
|
||||||
this.userResolver = s ->
|
this.userResolver = s ->
|
||||||
invokerRequest.userHomeDirectory().resolve(s).normalize().toAbsolutePath();
|
invokerRequest.userHomeDirectory().resolve(s).normalize().toAbsolutePath();
|
||||||
this.stdIn = invokerRequest.in().orElse(System.in);
|
|
||||||
this.stdOut = new PrintWriter(invokerRequest.out().orElse(System.out), true);
|
|
||||||
this.stdErr = new PrintWriter(invokerRequest.err().orElse(System.err), true);
|
|
||||||
this.logger = invokerRequest.parserRequest().logger();
|
this.logger = invokerRequest.parserRequest().logger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +142,8 @@ public abstract class LookupInvoker<
|
||||||
public ILoggerFactory loggerFactory;
|
public ILoggerFactory loggerFactory;
|
||||||
public Slf4jConfiguration slf4jConfiguration;
|
public Slf4jConfiguration slf4jConfiguration;
|
||||||
public Slf4jConfiguration.Level loggerLevel;
|
public Slf4jConfiguration.Level loggerLevel;
|
||||||
|
public Terminal terminal;
|
||||||
|
public BuildEventListener buildEventListener;
|
||||||
public ClassLoader currentThreadContextClassLoader;
|
public ClassLoader currentThreadContextClassLoader;
|
||||||
public ContainerCapsule containerCapsule;
|
public ContainerCapsule containerCapsule;
|
||||||
public Lookup lookup;
|
public Lookup lookup;
|
||||||
|
@ -149,10 +156,29 @@ public abstract class LookupInvoker<
|
||||||
public Path userSettingsPath;
|
public Path userSettingsPath;
|
||||||
public Settings effectiveSettings;
|
public Settings effectiveSettings;
|
||||||
|
|
||||||
|
public final List<AutoCloseable> closeables = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws InvokerException {
|
public void close() throws InvokerException {
|
||||||
if (containerCapsule != null) {
|
List<Exception> causes = null;
|
||||||
containerCapsule.close();
|
List<AutoCloseable> cs = new ArrayList<>(closeables);
|
||||||
|
Collections.reverse(cs);
|
||||||
|
for (AutoCloseable c : cs) {
|
||||||
|
if (c != null) {
|
||||||
|
try {
|
||||||
|
c.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (causes == null) {
|
||||||
|
causes = new ArrayList<>();
|
||||||
|
}
|
||||||
|
causes.add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (causes != null) {
|
||||||
|
InvokerException exception = new InvokerException("Unable to close context");
|
||||||
|
causes.forEach(exception::addSuppressed);
|
||||||
|
throw exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,20 +298,77 @@ public abstract class LookupInvoker<
|
||||||
// else fall back to default log level specified in conf
|
// else fall back to default log level specified in conf
|
||||||
// see https://issues.apache.org/jira/browse/MNG-2570
|
// see https://issues.apache.org/jira/browse/MNG-2570
|
||||||
|
|
||||||
// LOG STREAMS
|
// JLine is quite slow to start due to the native library unpacking and loading
|
||||||
if (mavenOptions.logFile().isPresent()) {
|
// so boot it asynchronously
|
||||||
Path logFile = context.cwdResolver.apply(mavenOptions.logFile().get());
|
context.terminal = createTerminal(context);
|
||||||
// redirect stdout and stderr to file
|
context.closeables.add(MessageUtils::systemUninstall);
|
||||||
try {
|
|
||||||
PrintStream ps = new PrintStream(Files.newOutputStream(logFile));
|
// Create the build log appender
|
||||||
System.setOut(ps);
|
ProjectBuildLogAppender projectBuildLogAppender =
|
||||||
System.setErr(ps);
|
new ProjectBuildLogAppender(determineBuildEventListener(context));
|
||||||
} catch (IOException e) {
|
context.closeables.add(projectBuildLogAppender);
|
||||||
throw new InvokerException("Cannot set up log " + e.getMessage(), e);
|
}
|
||||||
}
|
|
||||||
|
protected Terminal createTerminal(C context) {
|
||||||
|
return new FastTerminal(
|
||||||
|
() -> TerminalBuilder.builder()
|
||||||
|
.name("Maven")
|
||||||
|
.streams(
|
||||||
|
context.invokerRequest.in().orElse(null),
|
||||||
|
context.invokerRequest.out().orElse(null))
|
||||||
|
.dumb(true)
|
||||||
|
.build(),
|
||||||
|
terminal -> doConfigureWithTerminal(context, terminal));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doConfigureWithTerminal(C context, Terminal terminal) {
|
||||||
|
MessageUtils.systemInstall(terminal);
|
||||||
|
AnsiConsole.setTerminal(terminal);
|
||||||
|
AnsiConsole.systemInstall();
|
||||||
|
MessageUtils.registerShutdownHook(); // safety belt
|
||||||
|
|
||||||
|
O options = context.invokerRequest.options();
|
||||||
|
if (options.rawStreams().isEmpty() || !options.rawStreams().get()) {
|
||||||
|
MavenSimpleLogger stdout = (MavenSimpleLogger) context.loggerFactory.getLogger("stdout");
|
||||||
|
MavenSimpleLogger stderr = (MavenSimpleLogger) context.loggerFactory.getLogger("stderr");
|
||||||
|
stdout.setLogLevel(LocationAwareLogger.INFO_INT);
|
||||||
|
stderr.setLogLevel(LocationAwareLogger.INFO_INT);
|
||||||
|
System.setOut(new LoggingOutputStream(s -> stdout.info("[stdout] " + s)).printStream());
|
||||||
|
System.setErr(new LoggingOutputStream(s -> stderr.warn("[stderr] " + s)).printStream());
|
||||||
|
// no need to set them back, this is already handled by MessageUtils.systemUninstall() above
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected BuildEventListener determineBuildEventListener(C context) {
|
||||||
|
if (context.buildEventListener == null) {
|
||||||
|
context.buildEventListener = doDetermineBuildEventListener(context);
|
||||||
|
}
|
||||||
|
return context.buildEventListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BuildEventListener doDetermineBuildEventListener(C context) {
|
||||||
|
BuildEventListener bel;
|
||||||
|
O options = context.invokerRequest.options();
|
||||||
|
if (options.logFile().isPresent()) {
|
||||||
|
Path logFile = context.cwdResolver.apply(options.logFile().get());
|
||||||
|
try {
|
||||||
|
PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logFile));
|
||||||
|
bel = new SimpleBuildEventListener(printWriter::println);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new MavenException("Unable to redirect logging to " + logFile, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Given the terminal creation has been offloaded to a different thread,
|
||||||
|
// do not pass directory the terminal writer
|
||||||
|
bel = new SimpleBuildEventListener(msg -> {
|
||||||
|
PrintWriter pw = context.terminal.writer();
|
||||||
|
pw.println(msg);
|
||||||
|
pw.flush();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return bel;
|
||||||
|
}
|
||||||
|
|
||||||
protected void activateLogging(C context) throws Exception {
|
protected void activateLogging(C context) throws Exception {
|
||||||
R invokerRequest = context.invokerRequest;
|
R invokerRequest = context.invokerRequest;
|
||||||
Options mavenOptions = invokerRequest.options();
|
Options mavenOptions = invokerRequest.options();
|
||||||
|
@ -298,13 +381,19 @@ public abstract class LookupInvoker<
|
||||||
|
|
||||||
if (mavenOptions.failOnSeverity().isPresent()) {
|
if (mavenOptions.failOnSeverity().isPresent()) {
|
||||||
String logLevelThreshold = mavenOptions.failOnSeverity().get();
|
String logLevelThreshold = mavenOptions.failOnSeverity().get();
|
||||||
|
if (context.loggerFactory instanceof LogLevelRecorder recorder) {
|
||||||
if (context.loggerFactory instanceof MavenSlf4jWrapperFactory) {
|
LogLevelRecorder.Level level =
|
||||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder(logLevelThreshold);
|
switch (logLevelThreshold.toLowerCase(Locale.ENGLISH)) {
|
||||||
((MavenSlf4jWrapperFactory) context.loggerFactory).setLogLevelRecorder(logLevelRecorder);
|
case "warn", "warning" -> LogLevelRecorder.Level.WARN;
|
||||||
|
case "error" -> LogLevelRecorder.Level.ERROR;
|
||||||
|
default -> throw new IllegalArgumentException(
|
||||||
|
logLevelThreshold
|
||||||
|
+ " is not a valid log severity threshold. Valid severities are WARN/WARNING and ERROR.");
|
||||||
|
};
|
||||||
|
recorder.setMaxLevelAllowed(level);
|
||||||
context.logger.info("Enabled to break the build on log level " + logLevelThreshold + ".");
|
context.logger.info("Enabled to break the build on log level " + logLevelThreshold + ".");
|
||||||
} else {
|
} else {
|
||||||
context.logger.warn("Expected LoggerFactory to be of type '" + MavenSlf4jWrapperFactory.class.getName()
|
context.logger.warn("Expected LoggerFactory to be of type '" + LogLevelRecorder.class.getName()
|
||||||
+ "', but found '"
|
+ "', but found '"
|
||||||
+ context.loggerFactory.getClass().getName() + "' instead. "
|
+ context.loggerFactory.getClass().getName() + "' instead. "
|
||||||
+ "The --fail-on-severity flag will not take effect.");
|
+ "The --fail-on-severity flag will not take effect.");
|
||||||
|
@ -315,14 +404,14 @@ public abstract class LookupInvoker<
|
||||||
protected void helpOrVersionAndMayExit(C context) throws Exception {
|
protected void helpOrVersionAndMayExit(C context) throws Exception {
|
||||||
R invokerRequest = context.invokerRequest;
|
R invokerRequest = context.invokerRequest;
|
||||||
if (invokerRequest.options().help().isPresent()) {
|
if (invokerRequest.options().help().isPresent()) {
|
||||||
invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), context.stdOut);
|
invokerRequest.options().displayHelp(context.invokerRequest.parserRequest(), context.terminal.writer());
|
||||||
throw new ExitException(0);
|
throw new ExitException(0);
|
||||||
}
|
}
|
||||||
if (invokerRequest.options().showVersionAndExit().isPresent()) {
|
if (invokerRequest.options().showVersionAndExit().isPresent()) {
|
||||||
if (invokerRequest.options().quiet().orElse(false)) {
|
if (invokerRequest.options().quiet().orElse(false)) {
|
||||||
context.stdOut.println(CLIReportingUtils.showVersionMinimal());
|
context.terminal.writer().println(CLIReportingUtils.showVersionMinimal());
|
||||||
} else {
|
} else {
|
||||||
context.stdOut.println(CLIReportingUtils.showVersion());
|
context.terminal.writer().println(CLIReportingUtils.showVersion());
|
||||||
}
|
}
|
||||||
throw new ExitException(0);
|
throw new ExitException(0);
|
||||||
}
|
}
|
||||||
|
@ -331,12 +420,13 @@ public abstract class LookupInvoker<
|
||||||
protected void preCommands(C context) throws Exception {
|
protected void preCommands(C context) throws Exception {
|
||||||
Options mavenOptions = context.invokerRequest.options();
|
Options mavenOptions = context.invokerRequest.options();
|
||||||
if (mavenOptions.verbose().orElse(false) || mavenOptions.showVersion().orElse(false)) {
|
if (mavenOptions.verbose().orElse(false) || mavenOptions.showVersion().orElse(false)) {
|
||||||
context.stdOut.println(CLIReportingUtils.showVersion());
|
context.terminal.writer().println(CLIReportingUtils.showVersion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void container(C context) throws Exception {
|
protected void container(C context) throws Exception {
|
||||||
context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(context);
|
context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(context);
|
||||||
|
context.closeables.add(context.containerCapsule);
|
||||||
context.lookup = context.containerCapsule.getLookup();
|
context.lookup = context.containerCapsule.getLookup();
|
||||||
context.settingsBuilder = context.lookup.lookup(SettingsBuilder.class);
|
context.settingsBuilder = context.lookup.lookup(SettingsBuilder.class);
|
||||||
|
|
||||||
|
@ -704,7 +794,7 @@ public abstract class LookupInvoker<
|
||||||
} else if (context.interactive && !logFile) {
|
} else if (context.interactive && !logFile) {
|
||||||
return new SimplexTransferListener(new ConsoleMavenTransferListener(
|
return new SimplexTransferListener(new ConsoleMavenTransferListener(
|
||||||
context.invokerRequest.messageBuilderFactory(),
|
context.invokerRequest.messageBuilderFactory(),
|
||||||
context.stdOut,
|
context.terminal.writer(),
|
||||||
context.invokerRequest.options().verbose().orElse(false)));
|
context.invokerRequest.options().verbose().orElse(false)));
|
||||||
} else {
|
} else {
|
||||||
return new Slf4jMavenTransferListener();
|
return new Slf4jMavenTransferListener();
|
||||||
|
|
|
@ -56,6 +56,8 @@ import org.apache.maven.execution.ProfileActivation;
|
||||||
import org.apache.maven.execution.ProjectActivation;
|
import org.apache.maven.execution.ProjectActivation;
|
||||||
import org.apache.maven.jline.MessageUtils;
|
import org.apache.maven.jline.MessageUtils;
|
||||||
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
||||||
|
import org.apache.maven.logging.LoggingExecutionListener;
|
||||||
|
import org.apache.maven.logging.MavenTransferListener;
|
||||||
import org.apache.maven.model.building.ModelProcessor;
|
import org.apache.maven.model.building.ModelProcessor;
|
||||||
import org.apache.maven.project.MavenProject;
|
import org.apache.maven.project.MavenProject;
|
||||||
import org.apache.maven.settings.building.SettingsBuildingRequest;
|
import org.apache.maven.settings.building.SettingsBuildingRequest;
|
||||||
|
@ -65,6 +67,7 @@ import org.apache.maven.toolchain.building.ToolchainsBuilder;
|
||||||
import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
|
import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
|
||||||
import org.codehaus.plexus.PlexusContainer;
|
import org.codehaus.plexus.PlexusContainer;
|
||||||
import org.eclipse.aether.DefaultRepositoryCache;
|
import org.eclipse.aether.DefaultRepositoryCache;
|
||||||
|
import org.eclipse.aether.transfer.TransferListener;
|
||||||
|
|
||||||
import static java.util.Comparator.comparing;
|
import static java.util.Comparator.comparing;
|
||||||
import static org.apache.maven.cling.invoker.Utils.toProperties;
|
import static org.apache.maven.cling.invoker.Utils.toProperties;
|
||||||
|
@ -359,12 +362,17 @@ public abstract class DefaultMavenInvoker<
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ExecutionListener determineExecutionListener(C context) {
|
protected ExecutionListener determineExecutionListener(C context) {
|
||||||
ExecutionListener executionListener = new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
|
ExecutionListener listener = new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
|
||||||
if (context.eventSpyDispatcher != null) {
|
if (context.eventSpyDispatcher != null) {
|
||||||
return context.eventSpyDispatcher.chainListener(executionListener);
|
listener = context.eventSpyDispatcher.chainListener(listener);
|
||||||
} else {
|
|
||||||
return executionListener;
|
|
||||||
}
|
}
|
||||||
|
listener = new LoggingExecutionListener(listener, determineBuildEventListener(context));
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TransferListener determineTransferListener(C context, boolean noTransferProgress) {
|
||||||
|
TransferListener delegate = super.determineTransferListener(context, noTransferProgress);
|
||||||
|
return new MavenTransferListener(delegate, determineBuildEventListener(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String determineMakeBehavior(C context) {
|
protected String determineMakeBehavior(C context) {
|
||||||
|
|
|
@ -39,8 +39,11 @@ public abstract class MavenInvokerTestSupport<O extends MavenOptions, R extends
|
||||||
|
|
||||||
protected void invoke(Path cwd, Collection<String> goals) throws Exception {
|
protected void invoke(Path cwd, Collection<String> goals) throws Exception {
|
||||||
// works only in recent Maven4
|
// works only in recent Maven4
|
||||||
Assumptions.assumeTrue(Files.isRegularFile(
|
Assumptions.assumeTrue(
|
||||||
Paths.get(System.getProperty("maven.home")).resolve("conf").resolve("maven.properties")));
|
Files.isRegularFile(Paths.get(System.getProperty("maven.home"))
|
||||||
|
.resolve("conf")
|
||||||
|
.resolve("maven.properties")),
|
||||||
|
"${maven.home}/conf/maven.properties must be a file");
|
||||||
|
|
||||||
Files.createDirectory(cwd.resolve(".mvn"));
|
Files.createDirectory(cwd.resolve(".mvn"));
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ under the License.
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-slf4j-provider</artifactId>
|
<artifactId>maven-logging</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven.resolver</groupId>
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
|
|
@ -93,9 +93,6 @@ public class MultiThreadedBuilder implements Builder {
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(nThreads, new BuildThreadFactory());
|
ExecutorService executor = Executors.newFixedThreadPool(nThreads, new BuildThreadFactory());
|
||||||
CompletionService<ProjectSegment> service = new ExecutorCompletionService<>(executor);
|
CompletionService<ProjectSegment> service = new ExecutorCompletionService<>(executor);
|
||||||
|
|
||||||
// Currently disabled
|
|
||||||
ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( analyzer.getProjectBuilds(), System.out );
|
|
||||||
|
|
||||||
for (TaskSegment taskSegment : taskSegments) {
|
for (TaskSegment taskSegment : taskSegments) {
|
||||||
ProjectBuildList segmentProjectBuilds = projectBuilds.getByTaskSegment(taskSegment);
|
ProjectBuildList segmentProjectBuilds = projectBuilds.getByTaskSegment(taskSegment);
|
||||||
Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment(taskSegment);
|
Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment(taskSegment);
|
||||||
|
@ -103,7 +100,7 @@ public class MultiThreadedBuilder implements Builder {
|
||||||
ConcurrencyDependencyGraph analyzer =
|
ConcurrencyDependencyGraph analyzer =
|
||||||
new ConcurrencyDependencyGraph(segmentProjectBuilds, session.getProjectDependencyGraph());
|
new ConcurrencyDependencyGraph(segmentProjectBuilds, session.getProjectDependencyGraph());
|
||||||
multiThreadedProjectTaskSegmentBuild(
|
multiThreadedProjectTaskSegmentBuild(
|
||||||
analyzer, reactorContext, session, service, taskSegment, projectBuildMap, muxer);
|
analyzer, reactorContext, session, service, taskSegment, projectBuildMap);
|
||||||
if (reactorContext.getReactorBuildStatus().isHalted()) {
|
if (reactorContext.getReactorBuildStatus().isHalted()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -123,8 +120,7 @@ public class MultiThreadedBuilder implements Builder {
|
||||||
MavenSession rootSession,
|
MavenSession rootSession,
|
||||||
CompletionService<ProjectSegment> service,
|
CompletionService<ProjectSegment> service,
|
||||||
TaskSegment taskSegment,
|
TaskSegment taskSegment,
|
||||||
Map<MavenProject, ProjectSegment> projectBuildList,
|
Map<MavenProject, ProjectSegment> projectBuildList) {
|
||||||
ThreadOutputMuxer muxer) {
|
|
||||||
// gather artifactIds which are not unique so that the respective thread names can be extended with the groupId
|
// gather artifactIds which are not unique so that the respective thread names can be extended with the groupId
|
||||||
Set<String> duplicateArtifactIds = projectBuildList.keySet().stream()
|
Set<String> duplicateArtifactIds = projectBuildList.keySet().stream()
|
||||||
.map(MavenProject::getArtifactId)
|
.map(MavenProject::getArtifactId)
|
||||||
|
@ -139,8 +135,8 @@ public class MultiThreadedBuilder implements Builder {
|
||||||
for (MavenProject mavenProject : analyzer.getRootSchedulableBuilds()) {
|
for (MavenProject mavenProject : analyzer.getRootSchedulableBuilds()) {
|
||||||
ProjectSegment projectSegment = projectBuildList.get(mavenProject);
|
ProjectSegment projectSegment = projectBuildList.get(mavenProject);
|
||||||
logger.debug("Scheduling: {}", projectSegment.getProject());
|
logger.debug("Scheduling: {}", projectSegment.getProject());
|
||||||
Callable<ProjectSegment> cb = createBuildCallable(
|
Callable<ProjectSegment> cb =
|
||||||
rootSession, projectSegment, reactorContext, taskSegment, muxer, duplicateArtifactIds);
|
createBuildCallable(rootSession, projectSegment, reactorContext, taskSegment, duplicateArtifactIds);
|
||||||
service.submit(cb);
|
service.submit(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,12 +156,7 @@ public class MultiThreadedBuilder implements Builder {
|
||||||
ProjectSegment scheduledDependent = projectBuildList.get(mavenProject);
|
ProjectSegment scheduledDependent = projectBuildList.get(mavenProject);
|
||||||
logger.debug("Scheduling: {}", scheduledDependent);
|
logger.debug("Scheduling: {}", scheduledDependent);
|
||||||
Callable<ProjectSegment> cb = createBuildCallable(
|
Callable<ProjectSegment> cb = createBuildCallable(
|
||||||
rootSession,
|
rootSession, scheduledDependent, reactorContext, taskSegment, duplicateArtifactIds);
|
||||||
scheduledDependent,
|
|
||||||
reactorContext,
|
|
||||||
taskSegment,
|
|
||||||
muxer,
|
|
||||||
duplicateArtifactIds);
|
|
||||||
service.submit(cb);
|
service.submit(cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,7 +176,6 @@ public class MultiThreadedBuilder implements Builder {
|
||||||
final ProjectSegment projectBuild,
|
final ProjectSegment projectBuild,
|
||||||
final ReactorContext reactorContext,
|
final ReactorContext reactorContext,
|
||||||
final TaskSegment taskSegment,
|
final TaskSegment taskSegment,
|
||||||
final ThreadOutputMuxer muxer,
|
|
||||||
final Set<String> duplicateArtifactIds) {
|
final Set<String> duplicateArtifactIds) {
|
||||||
return () -> {
|
return () -> {
|
||||||
final Thread currentThread = Thread.currentThread();
|
final Thread currentThread = Thread.currentThread();
|
||||||
|
@ -198,10 +188,8 @@ public class MultiThreadedBuilder implements Builder {
|
||||||
currentThread.setName("mvn-builder-" + threadNameSuffix);
|
currentThread.setName("mvn-builder-" + threadNameSuffix);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// muxer.associateThreadWithProjectSegment( projectBuild );
|
|
||||||
lifecycleModuleBuilder.buildProject(
|
lifecycleModuleBuilder.buildProject(
|
||||||
projectBuild.getSession(), rootSession, reactorContext, project, taskSegment);
|
projectBuild.getSession(), rootSession, reactorContext, project, taskSegment);
|
||||||
// muxer.setThisModuleComplete( projectBuild );
|
|
||||||
|
|
||||||
return projectBuild;
|
return projectBuild;
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -1,390 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.maven.lifecycle.internal.builder.multithreaded;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.maven.lifecycle.internal.ProjectBuildList;
|
|
||||||
import org.apache.maven.lifecycle.internal.ProjectSegment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <strong>NOTE:</strong> This class is not part of any public api and can be changed or deleted without prior notice.
|
|
||||||
* This class in particular may spontaneously self-combust and be replaced by a plexus-compliant thread aware
|
|
||||||
* logger implementation at any time.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"})
|
|
||||||
public class ThreadOutputMuxer {
|
|
||||||
private final Iterator<ProjectSegment> projects;
|
|
||||||
|
|
||||||
private final ThreadLocal<ProjectSegment> projectBuildThreadLocal = new ThreadLocal<>();
|
|
||||||
|
|
||||||
private final Map<ProjectSegment, ByteArrayOutputStream> streams = new HashMap<>();
|
|
||||||
|
|
||||||
private final Map<ProjectSegment, PrintStream> printStreams = new HashMap<>();
|
|
||||||
|
|
||||||
private final ByteArrayOutputStream defaultOutputStreamForUnknownData = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
private final PrintStream defaultPrintStream = new PrintStream(defaultOutputStreamForUnknownData);
|
|
||||||
|
|
||||||
private final Set<ProjectSegment> completedBuilds = Collections.synchronizedSet(new HashSet<>());
|
|
||||||
|
|
||||||
private volatile ProjectSegment currentBuild;
|
|
||||||
|
|
||||||
private final PrintStream originalSystemOUtStream;
|
|
||||||
|
|
||||||
private final ConsolePrinter printer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple but safe solution for printing to the console.
|
|
||||||
*/
|
|
||||||
class ConsolePrinter implements Runnable {
|
|
||||||
private volatile boolean running;
|
|
||||||
|
|
||||||
private final ProjectBuildList projectBuildList;
|
|
||||||
|
|
||||||
ConsolePrinter(ProjectBuildList projectBuildList) {
|
|
||||||
this.projectBuildList = projectBuildList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
running = true;
|
|
||||||
for (ProjectSegment projectBuild : projectBuildList) {
|
|
||||||
final PrintStream projectStream = printStreams.get(projectBuild);
|
|
||||||
ByteArrayOutputStream projectOs = streams.get(projectBuild);
|
|
||||||
|
|
||||||
do {
|
|
||||||
synchronized (projectStream) {
|
|
||||||
try {
|
|
||||||
projectStream.wait(100);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
projectOs.writeTo(originalSystemOUtStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
projectOs.reset();
|
|
||||||
}
|
|
||||||
} while (!completedBuilds.contains(projectBuild));
|
|
||||||
}
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Wait until we are sure the print-stream thread is running.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void waitUntilRunning(boolean expect) {
|
|
||||||
while (!running == expect) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(10);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThreadOutputMuxer(ProjectBuildList segmentChunks, PrintStream originalSystemOut) {
|
|
||||||
projects = segmentChunks.iterator();
|
|
||||||
for (ProjectSegment segmentChunk : segmentChunks) {
|
|
||||||
final ByteArrayOutputStream value = new ByteArrayOutputStream();
|
|
||||||
streams.put(segmentChunk, value);
|
|
||||||
printStreams.put(segmentChunk, new PrintStream(value));
|
|
||||||
}
|
|
||||||
setNext();
|
|
||||||
this.originalSystemOUtStream = originalSystemOut;
|
|
||||||
System.setOut(new ThreadBoundPrintStream(this.originalSystemOUtStream));
|
|
||||||
printer = new ConsolePrinter(segmentChunks);
|
|
||||||
new Thread(printer).start();
|
|
||||||
printer.waitUntilRunning(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
printer.waitUntilRunning(false);
|
|
||||||
System.setOut(this.originalSystemOUtStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setNext() {
|
|
||||||
currentBuild = projects.hasNext() ? projects.next() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean ownsRealOutputStream(ProjectSegment projectBuild) {
|
|
||||||
return projectBuild.equals(currentBuild);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PrintStream getThreadBoundPrintStream() {
|
|
||||||
ProjectSegment threadProject = projectBuildThreadLocal.get();
|
|
||||||
if (threadProject == null) {
|
|
||||||
return defaultPrintStream;
|
|
||||||
}
|
|
||||||
if (ownsRealOutputStream(threadProject)) {
|
|
||||||
return originalSystemOUtStream;
|
|
||||||
}
|
|
||||||
return printStreams.get(threadProject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void associateThreadWithProjectSegment(ProjectSegment projectBuild) {
|
|
||||||
projectBuildThreadLocal.set(projectBuild);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setThisModuleComplete(ProjectSegment projectBuild) {
|
|
||||||
completedBuilds.add(projectBuild);
|
|
||||||
PrintStream stream = printStreams.get(projectBuild);
|
|
||||||
synchronized (stream) {
|
|
||||||
stream.notifyAll();
|
|
||||||
}
|
|
||||||
disconnectThreadFromProject();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disconnectThreadFromProject() {
|
|
||||||
projectBuildThreadLocal.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ThreadBoundPrintStream extends PrintStream {
|
|
||||||
|
|
||||||
ThreadBoundPrintStream(PrintStream systemOutStream) {
|
|
||||||
super(systemOutStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PrintStream getOutputStreamForCurrentThread() {
|
|
||||||
return getThreadBoundPrintStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println() {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.println();
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(char c) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(c);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(char x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.println(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(double d) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(d);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(double x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.println(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(float f) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(f);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(float x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.println(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(int i) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(i);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(int x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.println(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(long l) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(l);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(long x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(boolean b) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(b);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(boolean x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(char s[]) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(s);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(char x[]) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(Object obj) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(obj);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(Object x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.println(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(String s) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.print(s);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void println(String x) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.println(x);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte b[], int off, int len) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.write(b, off, len);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
getOutputStreamForCurrentThread().close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() {
|
|
||||||
getOutputStreamForCurrentThread().flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(int b) {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.write(b);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(byte b[]) throws IOException {
|
|
||||||
final PrintStream currentStream = getOutputStreamForCurrentThread();
|
|
||||||
synchronized (currentStream) {
|
|
||||||
currentStream.write(b);
|
|
||||||
currentStream.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -164,7 +164,6 @@ public class BuildPlanExecutor {
|
||||||
final MavenSession session;
|
final MavenSession session;
|
||||||
final ReactorContext reactorContext;
|
final ReactorContext reactorContext;
|
||||||
final PhasingExecutor executor;
|
final PhasingExecutor executor;
|
||||||
final ConcurrentLogOutput appender;
|
|
||||||
final Map<Object, Clock> clocks = new ConcurrentHashMap<>();
|
final Map<Object, Clock> clocks = new ConcurrentHashMap<>();
|
||||||
final ReadWriteLock lock = new ReentrantReadWriteLock();
|
final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
final int threads;
|
final int threads;
|
||||||
|
@ -179,7 +178,6 @@ public class BuildPlanExecutor {
|
||||||
// Propagate the parallel flag to the root session
|
// Propagate the parallel flag to the root session
|
||||||
session.setParallel(threads > 1);
|
session.setParallel(threads > 1);
|
||||||
this.executor = new PhasingExecutor(Executors.newFixedThreadPool(threads, new BuildThreadFactory()));
|
this.executor = new PhasingExecutor(Executors.newFixedThreadPool(threads, new BuildThreadFactory()));
|
||||||
this.appender = new ConcurrentLogOutput();
|
|
||||||
|
|
||||||
// build initial plan
|
// build initial plan
|
||||||
this.plan = buildInitialPlan(taskSegments);
|
this.plan = buildInitialPlan(taskSegments);
|
||||||
|
@ -190,7 +188,6 @@ public class BuildPlanExecutor {
|
||||||
this.reactorContext = null;
|
this.reactorContext = null;
|
||||||
this.threads = 1;
|
this.threads = 1;
|
||||||
this.executor = null;
|
this.executor = null;
|
||||||
this.appender = null;
|
|
||||||
this.plan = null;
|
this.plan = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +309,6 @@ public class BuildPlanExecutor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
this.appender.close();
|
|
||||||
this.executor.close();
|
this.executor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +327,7 @@ public class BuildPlanExecutor {
|
||||||
.forEach(step -> {
|
.forEach(step -> {
|
||||||
boolean nextIsPlanning = step.successors.stream().anyMatch(st -> PLAN.equals(st.name));
|
boolean nextIsPlanning = step.successors.stream().anyMatch(st -> PLAN.equals(st.name));
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
try (AutoCloseable ctx = appender.build(step.project)) {
|
try {
|
||||||
executeStep(step);
|
executeStep(step);
|
||||||
if (nextIsPlanning) {
|
if (nextIsPlanning) {
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.maven.lifecycle.internal.concurrent;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
import org.apache.maven.project.MavenProject;
|
|
||||||
import org.apache.maven.slf4j.MavenSimpleLogger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forwards log messages to the client.
|
|
||||||
*/
|
|
||||||
public class ConcurrentLogOutput implements AutoCloseable {
|
|
||||||
|
|
||||||
private static final ThreadLocal<ProjectExecutionContext> CONTEXT = new InheritableThreadLocal<>();
|
|
||||||
|
|
||||||
public ConcurrentLogOutput() {
|
|
||||||
MavenSimpleLogger.setLogSink(this::accept);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void accept(String message) {
|
|
||||||
ProjectExecutionContext context = CONTEXT.get();
|
|
||||||
if (context != null) {
|
|
||||||
context.accept(message);
|
|
||||||
} else {
|
|
||||||
System.out.println(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
MavenSimpleLogger.setLogSink(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AutoCloseable build(MavenProject project) {
|
|
||||||
return new ProjectExecutionContext(project);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ProjectExecutionContext implements AutoCloseable {
|
|
||||||
final MavenProject project;
|
|
||||||
final List<String> messages = new CopyOnWriteArrayList<>();
|
|
||||||
boolean closed;
|
|
||||||
|
|
||||||
ProjectExecutionContext(MavenProject project) {
|
|
||||||
this.project = project;
|
|
||||||
CONTEXT.set(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void accept(String message) {
|
|
||||||
if (!closed) {
|
|
||||||
this.messages.add(message);
|
|
||||||
} else {
|
|
||||||
System.out.println(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
closed = true;
|
|
||||||
CONTEXT.set(null);
|
|
||||||
this.messages.forEach(System.out::println);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,17 +16,33 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.logwrapper;
|
package org.apache.maven.logging;
|
||||||
|
|
||||||
import java.util.Optional;
|
import org.apache.maven.execution.ExecutionEvent;
|
||||||
|
import org.eclipse.aether.transfer.TransferEvent;
|
||||||
import org.slf4j.ILoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for creating loggers which can have a log level threshold.
|
* An abstract build event sink.
|
||||||
*/
|
*/
|
||||||
public interface MavenSlf4jWrapperFactory extends ILoggerFactory {
|
public interface BuildEventListener {
|
||||||
void setLogLevelRecorder(LogLevelRecorder logLevelRecorder);
|
|
||||||
|
|
||||||
Optional<LogLevelRecorder> getLogLevelRecorder();
|
void sessionStarted(ExecutionEvent event);
|
||||||
|
|
||||||
|
void projectStarted(String projectId);
|
||||||
|
|
||||||
|
void projectLogMessage(String projectId, String event);
|
||||||
|
|
||||||
|
void projectFinished(String projectId);
|
||||||
|
|
||||||
|
void executionFailure(String projectId, boolean halted, String exception);
|
||||||
|
|
||||||
|
void mojoStarted(ExecutionEvent event);
|
||||||
|
|
||||||
|
void finish(int exitCode) throws Exception;
|
||||||
|
|
||||||
|
void fail(Throwable t) throws Exception;
|
||||||
|
|
||||||
|
void log(String msg);
|
||||||
|
|
||||||
|
void transfer(String projectId, TransferEvent e);
|
||||||
}
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.logging;
|
||||||
|
|
||||||
|
import org.apache.maven.execution.ExecutionEvent;
|
||||||
|
import org.apache.maven.execution.ExecutionListener;
|
||||||
|
import org.apache.maven.execution.MavenExecutionRequest;
|
||||||
|
import org.apache.maven.execution.MavenSession;
|
||||||
|
import org.apache.maven.execution.ProjectExecutionEvent;
|
||||||
|
import org.apache.maven.execution.ProjectExecutionListener;
|
||||||
|
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
||||||
|
import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
|
||||||
|
|
||||||
|
public class LoggingExecutionListener implements ExecutionListener, ProjectExecutionListener {
|
||||||
|
|
||||||
|
private final ExecutionListener delegate;
|
||||||
|
private final BuildEventListener buildEventListener;
|
||||||
|
|
||||||
|
public LoggingExecutionListener(ExecutionListener delegate, BuildEventListener buildEventListener) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.buildEventListener = buildEventListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeProjectExecution(ProjectExecutionEvent projectExecutionEvent)
|
||||||
|
throws LifecycleExecutionException {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeProjectLifecycleExecution(ProjectExecutionEvent projectExecutionEvent)
|
||||||
|
throws LifecycleExecutionException {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterProjectExecutionSuccess(ProjectExecutionEvent projectExecutionEvent)
|
||||||
|
throws LifecycleExecutionException {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterProjectExecutionFailure(ProjectExecutionEvent projectExecutionEvent) {
|
||||||
|
MavenSession session = projectExecutionEvent.getSession();
|
||||||
|
boolean halted;
|
||||||
|
// The ReactorBuildStatus is only available if the SmartBuilder is used
|
||||||
|
ReactorBuildStatus status =
|
||||||
|
(ReactorBuildStatus) session.getRepositorySession().getData().get(ReactorBuildStatus.class);
|
||||||
|
if (status != null) {
|
||||||
|
halted = status.isHalted();
|
||||||
|
} else {
|
||||||
|
// assume sensible default
|
||||||
|
Throwable t = projectExecutionEvent.getCause();
|
||||||
|
halted = (t instanceof RuntimeException || !(t instanceof Exception))
|
||||||
|
|| !MavenExecutionRequest.REACTOR_FAIL_NEVER.equals(session.getReactorFailureBehavior())
|
||||||
|
&& !MavenExecutionRequest.REACTOR_FAIL_AT_END.equals(session.getReactorFailureBehavior());
|
||||||
|
}
|
||||||
|
Throwable cause = projectExecutionEvent.getCause();
|
||||||
|
buildEventListener.executionFailure(
|
||||||
|
projectExecutionEvent.getProject().getArtifactId(), halted, cause != null ? cause.toString() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectDiscoveryStarted(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.projectDiscoveryStarted(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sessionStarted(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
buildEventListener.sessionStarted(event);
|
||||||
|
delegate.sessionStarted(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sessionEnded(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.sessionEnded(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectStarted(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
buildEventListener.projectStarted(event.getProject().getArtifactId());
|
||||||
|
delegate.projectStarted(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectSucceeded(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.projectSucceeded(event);
|
||||||
|
buildEventListener.projectFinished(event.getProject().getArtifactId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectFailed(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.projectFailed(event);
|
||||||
|
buildEventListener.projectFinished(event.getProject().getArtifactId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectSkipped(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
buildEventListener.projectStarted(event.getProject().getArtifactId());
|
||||||
|
delegate.projectSkipped(event);
|
||||||
|
buildEventListener.projectFinished(event.getProject().getArtifactId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mojoStarted(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
buildEventListener.mojoStarted(event);
|
||||||
|
delegate.mojoStarted(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mojoSucceeded(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.mojoSucceeded(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mojoFailed(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.mojoFailed(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mojoSkipped(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.mojoSkipped(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forkStarted(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.forkStarted(event);
|
||||||
|
ProjectBuildLogAppender.setForkingProjectId(event.getProject().getArtifactId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forkSucceeded(ExecutionEvent event) {
|
||||||
|
delegate.forkSucceeded(event);
|
||||||
|
ProjectBuildLogAppender.setForkingProjectId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forkFailed(ExecutionEvent event) {
|
||||||
|
delegate.forkFailed(event);
|
||||||
|
ProjectBuildLogAppender.setForkingProjectId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forkedProjectStarted(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.forkedProjectStarted(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forkedProjectSucceeded(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.forkedProjectSucceeded(event);
|
||||||
|
ProjectBuildLogAppender.setProjectId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forkedProjectFailed(ExecutionEvent event) {
|
||||||
|
setMdc(event);
|
||||||
|
delegate.forkedProjectFailed(event);
|
||||||
|
ProjectBuildLogAppender.setProjectId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setMdc(ExecutionEvent event) {
|
||||||
|
if (event.getProject() != null) {
|
||||||
|
ProjectBuildLogAppender.setProjectId(event.getProject().getArtifactId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.logging;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class LoggingOutputStream extends FilterOutputStream {
|
||||||
|
|
||||||
|
static final byte[] LINE_SEP = System.lineSeparator().getBytes();
|
||||||
|
|
||||||
|
final EolBaos buf;
|
||||||
|
final Consumer<String> consumer;
|
||||||
|
|
||||||
|
public LoggingOutputStream(Consumer<String> consumer) {
|
||||||
|
this(new EolBaos(), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggingOutputStream(EolBaos out, Consumer<String> consumer) {
|
||||||
|
super(out);
|
||||||
|
this.buf = out;
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintStream printStream() {
|
||||||
|
return new LoggingPrintStream(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
super.write(b);
|
||||||
|
if (buf.isEol()) {
|
||||||
|
String line = new String(buf.toByteArray(), 0, buf.size() - LINE_SEP.length);
|
||||||
|
ProjectBuildLogAppender.updateMdc();
|
||||||
|
consumer.accept(line);
|
||||||
|
buf.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forceFlush() {
|
||||||
|
if (buf.size() > 0) {
|
||||||
|
String line = new String(buf.toByteArray(), 0, buf.size());
|
||||||
|
ProjectBuildLogAppender.updateMdc();
|
||||||
|
consumer.accept(line);
|
||||||
|
buf.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EolBaos extends ByteArrayOutputStream {
|
||||||
|
boolean isEol() {
|
||||||
|
if (count >= LINE_SEP.length) {
|
||||||
|
for (int i = 0; i < LINE_SEP.length; i++) {
|
||||||
|
if (buf[count - LINE_SEP.length + i] != LINE_SEP[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LoggingPrintStream extends PrintStream {
|
||||||
|
public LoggingPrintStream(LoggingOutputStream out) {
|
||||||
|
super(out, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forceFlush() {
|
||||||
|
((LoggingOutputStream) out).forceFlush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void forceFlush(PrintStream ps) {
|
||||||
|
if (ps instanceof LoggingPrintStream) {
|
||||||
|
((LoggingPrintStream) ps).forceFlush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.logging;
|
||||||
|
|
||||||
|
import org.eclipse.aether.transfer.TransferCancelledException;
|
||||||
|
import org.eclipse.aether.transfer.TransferEvent;
|
||||||
|
import org.eclipse.aether.transfer.TransferListener;
|
||||||
|
|
||||||
|
public class MavenTransferListener implements TransferListener {
|
||||||
|
|
||||||
|
private final TransferListener delegate;
|
||||||
|
private final BuildEventListener dispatcher;
|
||||||
|
|
||||||
|
public MavenTransferListener(TransferListener delegate, BuildEventListener dispatcher) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferInitiated(TransferEvent event) throws TransferCancelledException {
|
||||||
|
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
|
||||||
|
delegate.transferInitiated(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferStarted(TransferEvent event) throws TransferCancelledException {
|
||||||
|
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
|
||||||
|
delegate.transferStarted(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferProgressed(TransferEvent event) throws TransferCancelledException {
|
||||||
|
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
|
||||||
|
delegate.transferProgressed(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferCorrupted(TransferEvent event) throws TransferCancelledException {
|
||||||
|
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
|
||||||
|
delegate.transferCorrupted(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferSucceeded(TransferEvent event) {
|
||||||
|
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
|
||||||
|
delegate.transferSucceeded(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferFailed(TransferEvent event) {
|
||||||
|
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
|
||||||
|
delegate.transferFailed(event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.logging;
|
||||||
|
|
||||||
|
import org.apache.maven.slf4j.MavenSimpleLogger;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards log messages to the client.
|
||||||
|
*/
|
||||||
|
public class ProjectBuildLogAppender implements AutoCloseable {
|
||||||
|
|
||||||
|
private static final String KEY_PROJECT_ID = "maven.project.id";
|
||||||
|
private static final ThreadLocal<String> PROJECT_ID = new InheritableThreadLocal<>();
|
||||||
|
private static final ThreadLocal<String> FORKING_PROJECT_ID = new InheritableThreadLocal<>();
|
||||||
|
|
||||||
|
public static String getProjectId() {
|
||||||
|
return PROJECT_ID.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setProjectId(String projectId) {
|
||||||
|
String forkingProjectId = FORKING_PROJECT_ID.get();
|
||||||
|
if (forkingProjectId != null) {
|
||||||
|
if (projectId != null) {
|
||||||
|
projectId = forkingProjectId + "/" + projectId;
|
||||||
|
} else {
|
||||||
|
projectId = forkingProjectId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (projectId != null) {
|
||||||
|
PROJECT_ID.set(projectId);
|
||||||
|
MDC.put(KEY_PROJECT_ID, projectId);
|
||||||
|
} else {
|
||||||
|
PROJECT_ID.remove();
|
||||||
|
MDC.remove(KEY_PROJECT_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setForkingProjectId(String forkingProjectId) {
|
||||||
|
if (forkingProjectId != null) {
|
||||||
|
FORKING_PROJECT_ID.set(forkingProjectId);
|
||||||
|
} else {
|
||||||
|
FORKING_PROJECT_ID.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateMdc() {
|
||||||
|
String id = getProjectId();
|
||||||
|
if (id != null) {
|
||||||
|
MDC.put(KEY_PROJECT_ID, id);
|
||||||
|
} else {
|
||||||
|
MDC.remove(KEY_PROJECT_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BuildEventListener buildEventListener;
|
||||||
|
|
||||||
|
public ProjectBuildLogAppender(BuildEventListener buildEventListener) {
|
||||||
|
this.buildEventListener = buildEventListener;
|
||||||
|
MavenSimpleLogger.setLogSink(this::accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void accept(String message) {
|
||||||
|
String projectId = MDC.get(KEY_PROJECT_ID);
|
||||||
|
buildEventListener.projectLogMessage(projectId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
MavenSimpleLogger.setLogSink(null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.logging;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.apache.maven.execution.ExecutionEvent;
|
||||||
|
import org.eclipse.aether.transfer.TransferEvent;
|
||||||
|
|
||||||
|
public class SimpleBuildEventListener implements BuildEventListener {
|
||||||
|
|
||||||
|
final Consumer<String> output;
|
||||||
|
|
||||||
|
public SimpleBuildEventListener(Consumer<String> output) {
|
||||||
|
this.output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sessionStarted(ExecutionEvent event) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectStarted(String projectId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectLogMessage(String projectId, String event) {
|
||||||
|
log(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void projectFinished(String projectId) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executionFailure(String projectId, boolean halted, String exception) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mojoStarted(ExecutionEvent event) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish(int exitCode) throws Exception {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fail(Throwable t) throws Exception {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void log(String msg) {
|
||||||
|
output.accept(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transfer(String projectId, TransferEvent e) {}
|
||||||
|
}
|
|
@ -44,8 +44,7 @@ public class PluginResolutionException extends Exception {
|
||||||
+ System.lineSeparator() + "\t"
|
+ System.lineSeparator() + "\t"
|
||||||
+ exceptions.stream()
|
+ exceptions.stream()
|
||||||
.map(Throwable::getMessage)
|
.map(Throwable::getMessage)
|
||||||
.collect(Collectors.joining(System.lineSeparator() + "\t"))
|
.collect(Collectors.joining(System.lineSeparator() + "\t")),
|
||||||
+ System.lineSeparator(),
|
|
||||||
cause);
|
cause);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,152 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.maven.lifecycle.internal.builder.multithreaded;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.CompletionService;
|
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
import org.apache.maven.execution.MavenSession;
|
|
||||||
import org.apache.maven.lifecycle.LifecycleNotFoundException;
|
|
||||||
import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
|
|
||||||
import org.apache.maven.lifecycle.internal.ProjectBuildList;
|
|
||||||
import org.apache.maven.lifecycle.internal.ProjectSegment;
|
|
||||||
import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;
|
|
||||||
import org.apache.maven.plugin.InvalidPluginDescriptorException;
|
|
||||||
import org.apache.maven.plugin.MojoNotFoundException;
|
|
||||||
import org.apache.maven.plugin.PluginDescriptorParsingException;
|
|
||||||
import org.apache.maven.plugin.PluginNotFoundException;
|
|
||||||
import org.apache.maven.plugin.PluginResolutionException;
|
|
||||||
import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
|
|
||||||
import org.apache.maven.plugin.version.PluginVersionResolutionException;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
class ThreadOutputMuxerTest {
|
|
||||||
|
|
||||||
final String paid = "Paid";
|
|
||||||
|
|
||||||
final String in = "In";
|
|
||||||
|
|
||||||
final String full = "Full";
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testSingleThreaded() throws Exception {
|
|
||||||
ProjectBuildList src = getProjectBuildList();
|
|
||||||
ProjectBuildList projectBuildList = new ProjectBuildList(Arrays.asList(src.get(0), src.get(1), src.get(2)));
|
|
||||||
|
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
||||||
PrintStream systemOut = new PrintStream(byteArrayOutputStream);
|
|
||||||
ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer(projectBuildList, systemOut);
|
|
||||||
|
|
||||||
threadOutputMuxer.associateThreadWithProjectSegment(projectBuildList.get(0));
|
|
||||||
System.out.print(paid); // No, this does not print to system.out. It's part of the test
|
|
||||||
assertEquals(paid.length(), byteArrayOutputStream.size());
|
|
||||||
threadOutputMuxer.associateThreadWithProjectSegment(projectBuildList.get(1));
|
|
||||||
System.out.print(in); // No, this does not print to system.out. It's part of the test
|
|
||||||
assertEquals(paid.length(), byteArrayOutputStream.size());
|
|
||||||
threadOutputMuxer.associateThreadWithProjectSegment(projectBuildList.get(2));
|
|
||||||
System.out.print(full); // No, this does not print to system.out. It's part of the test
|
|
||||||
assertEquals(paid.length(), byteArrayOutputStream.size());
|
|
||||||
|
|
||||||
threadOutputMuxer.setThisModuleComplete(projectBuildList.get(0));
|
|
||||||
threadOutputMuxer.setThisModuleComplete(projectBuildList.get(1));
|
|
||||||
threadOutputMuxer.setThisModuleComplete(projectBuildList.get(2));
|
|
||||||
threadOutputMuxer.close();
|
|
||||||
assertEquals((paid + in + full).length(), byteArrayOutputStream.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testMultiThreaded() throws Exception {
|
|
||||||
ProjectBuildList projectBuildList = getProjectBuildList();
|
|
||||||
|
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
||||||
PrintStream systemOut = new PrintStream(byteArrayOutputStream);
|
|
||||||
final ThreadOutputMuxer threadOutputMuxer = new ThreadOutputMuxer(projectBuildList, systemOut);
|
|
||||||
|
|
||||||
final List<String> stringList = Arrays.asList(
|
|
||||||
"Thinkin", "of", "a", "master", "plan", "Cuz", "ain’t", "nuthin", "but", "sweat", "inside", "my",
|
|
||||||
"hand");
|
|
||||||
Iterator<String> lyrics = stringList.iterator();
|
|
||||||
|
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(10);
|
|
||||||
CompletionService<ProjectSegment> service = new ExecutorCompletionService<>(executor);
|
|
||||||
|
|
||||||
List<Future<ProjectSegment>> futures = new ArrayList<>();
|
|
||||||
for (ProjectSegment projectBuild : projectBuildList) {
|
|
||||||
final Future<ProjectSegment> buildFuture =
|
|
||||||
service.submit(new Outputter(threadOutputMuxer, projectBuild, lyrics.next()));
|
|
||||||
futures.add(buildFuture);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Future<ProjectSegment> future : futures) {
|
|
||||||
future.get();
|
|
||||||
}
|
|
||||||
int expectedLength = 0;
|
|
||||||
for (int i = 0; i < projectBuildList.size(); i++) {
|
|
||||||
expectedLength += stringList.get(i).length();
|
|
||||||
}
|
|
||||||
|
|
||||||
threadOutputMuxer.close();
|
|
||||||
final byte[] bytes = byteArrayOutputStream.toByteArray();
|
|
||||||
String result = new String(bytes);
|
|
||||||
assertEquals(expectedLength, bytes.length, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Outputter implements Callable<ProjectSegment> {
|
|
||||||
private final ThreadOutputMuxer threadOutputMuxer;
|
|
||||||
|
|
||||||
private final ProjectSegment item;
|
|
||||||
|
|
||||||
private final String response;
|
|
||||||
|
|
||||||
Outputter(ThreadOutputMuxer threadOutputMuxer, ProjectSegment item, String response) {
|
|
||||||
this.threadOutputMuxer = threadOutputMuxer;
|
|
||||||
this.item = item;
|
|
||||||
this.response = response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProjectSegment call() throws Exception {
|
|
||||||
threadOutputMuxer.associateThreadWithProjectSegment(item);
|
|
||||||
System.out.print(response);
|
|
||||||
threadOutputMuxer.setThisModuleComplete(item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProjectBuildList getProjectBuildList()
|
|
||||||
throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException,
|
|
||||||
NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException,
|
|
||||||
PluginResolutionException, LifecyclePhaseNotFoundException, LifecycleNotFoundException {
|
|
||||||
final MavenSession session = ProjectDependencyGraphStub.getMavenSession();
|
|
||||||
return ProjectDependencyGraphStub.getProjectBuildList(session);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -74,7 +74,7 @@ under the License.
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
<artifactId>maven-logging</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven.resolver</groupId>
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -99,8 +100,7 @@ import org.apache.maven.extension.internal.CoreExtensionEntry;
|
||||||
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
||||||
import org.apache.maven.jline.MessageUtils;
|
import org.apache.maven.jline.MessageUtils;
|
||||||
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
||||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
import org.apache.maven.logging.api.LogLevelRecorder;
|
||||||
import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
|
|
||||||
import org.apache.maven.model.building.ModelProcessor;
|
import org.apache.maven.model.building.ModelProcessor;
|
||||||
import org.apache.maven.model.root.RootLocator;
|
import org.apache.maven.model.root.RootLocator;
|
||||||
import org.apache.maven.project.MavenProject;
|
import org.apache.maven.project.MavenProject;
|
||||||
|
@ -541,15 +541,22 @@ public class MavenCli {
|
||||||
if (commandLine.hasOption(CLIManager.FAIL_ON_SEVERITY)) {
|
if (commandLine.hasOption(CLIManager.FAIL_ON_SEVERITY)) {
|
||||||
String logLevelThreshold = commandLine.getOptionValue(CLIManager.FAIL_ON_SEVERITY);
|
String logLevelThreshold = commandLine.getOptionValue(CLIManager.FAIL_ON_SEVERITY);
|
||||||
|
|
||||||
if (slf4jLoggerFactory instanceof MavenSlf4jWrapperFactory) {
|
if (slf4jLoggerFactory instanceof LogLevelRecorder recorder) {
|
||||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder(logLevelThreshold);
|
LogLevelRecorder.Level level =
|
||||||
((MavenSlf4jWrapperFactory) slf4jLoggerFactory).setLogLevelRecorder(logLevelRecorder);
|
switch (logLevelThreshold.toLowerCase(Locale.ENGLISH)) {
|
||||||
|
case "warn", "warning" -> LogLevelRecorder.Level.WARN;
|
||||||
|
case "error" -> LogLevelRecorder.Level.ERROR;
|
||||||
|
default -> throw new IllegalArgumentException(
|
||||||
|
logLevelThreshold
|
||||||
|
+ " is not a valid log severity threshold. Valid severities are WARN/WARNING and ERROR.");
|
||||||
|
};
|
||||||
|
recorder.setMaxLevelAllowed(level);
|
||||||
slf4jLogger.info("Enabled to break the build on log level {}.", logLevelThreshold);
|
slf4jLogger.info("Enabled to break the build on log level {}.", logLevelThreshold);
|
||||||
} else {
|
} else {
|
||||||
slf4jLogger.warn(
|
slf4jLogger.warn(
|
||||||
"Expected LoggerFactory to be of type '{}', but found '{}' instead. "
|
"Expected LoggerFactory to be of type '{}', but found '{}' instead. "
|
||||||
+ "The --fail-on-severity flag will not take effect.",
|
+ "The --fail-on-severity flag will not take effect.",
|
||||||
MavenSlf4jWrapperFactory.class.getName(),
|
LogLevelRecorder.class.getName(),
|
||||||
slf4jLoggerFactory.getClass().getName());
|
slf4jLoggerFactory.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,6 @@ import org.apache.maven.execution.BuildSummary;
|
||||||
import org.apache.maven.execution.ExecutionEvent;
|
import org.apache.maven.execution.ExecutionEvent;
|
||||||
import org.apache.maven.execution.MavenExecutionResult;
|
import org.apache.maven.execution.MavenExecutionResult;
|
||||||
import org.apache.maven.execution.MavenSession;
|
import org.apache.maven.execution.MavenSession;
|
||||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
|
||||||
import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
|
|
||||||
import org.apache.maven.plugin.MojoExecution;
|
import org.apache.maven.plugin.MojoExecution;
|
||||||
import org.apache.maven.plugin.descriptor.MojoDescriptor;
|
import org.apache.maven.plugin.descriptor.MojoDescriptor;
|
||||||
import org.apache.maven.project.MavenProject;
|
import org.apache.maven.project.MavenProject;
|
||||||
|
@ -79,13 +77,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String chars(char c, int count) {
|
private static String chars(char c, int count) {
|
||||||
StringBuilder buffer = new StringBuilder(count);
|
return String.valueOf(c).repeat(Math.max(0, count));
|
||||||
|
|
||||||
for (int i = count; i > 0; i--) {
|
|
||||||
buffer.append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void infoLine(char c) {
|
private void infoLine(char c) {
|
||||||
|
@ -137,9 +129,8 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<MavenProject> allProjects = event.getSession().getAllProjects();
|
final List<MavenProject> allProjects = event.getSession().getAllProjects();
|
||||||
final int projectsSkipped = allProjects.size() - projects.size();
|
|
||||||
|
|
||||||
currentVisitedProjectCount = projectsSkipped;
|
currentVisitedProjectCount = allProjects.size() - projects.size();
|
||||||
totalProjects = allProjects.size();
|
totalProjects = allProjects.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,16 +145,13 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
||||||
|
|
||||||
ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
|
ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
|
||||||
|
|
||||||
if (iLoggerFactory instanceof MavenSlf4jWrapperFactory) {
|
if (iLoggerFactory instanceof org.apache.maven.logging.api.LogLevelRecorder recorder
|
||||||
MavenSlf4jWrapperFactory loggerFactory = (MavenSlf4jWrapperFactory) iLoggerFactory;
|
&& recorder.hasReachedMaxLevel()) {
|
||||||
loggerFactory
|
event.getSession()
|
||||||
.getLogLevelRecorder()
|
.getResult()
|
||||||
.filter(LogLevelRecorder::metThreshold)
|
.addException(
|
||||||
.ifPresent(recorder -> event.getSession()
|
new Exception("Build failed due to log statements with a higher severity than allowed. "
|
||||||
.getResult()
|
+ "Fix the logged issues or remove flag --fail-on-severity (-fos)."));
|
||||||
.addException(new Exception(
|
|
||||||
"Build failed due to log statements with a higher severity than allowed. "
|
|
||||||
+ "Fix the logged issues or remove flag --fail-on-severity (-fos).")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logResult(event.getSession());
|
logResult(event.getSession());
|
||||||
|
@ -297,7 +285,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
||||||
infoLine('-');
|
infoLine('-');
|
||||||
String name = event.getProject().getName();
|
String name = event.getProject().getName();
|
||||||
infoMain("Skipping " + name);
|
infoMain("Skipping " + name);
|
||||||
logger.info(name + " was not built because a module it depends on failed to build.");
|
logger.info("{} was not built because a module it depends on failed to build.", name);
|
||||||
|
|
||||||
infoLine('-');
|
infoLine('-');
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.cli.logging;
|
package org.apache.maven.cli.logging;
|
||||||
|
|
||||||
|
import org.apache.maven.logging.ProjectBuildLogAppender;
|
||||||
import org.codehaus.plexus.logging.Logger;
|
import org.codehaus.plexus.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,16 +30,20 @@ import org.codehaus.plexus.logging.Logger;
|
||||||
public class Slf4jLogger implements Logger {
|
public class Slf4jLogger implements Logger {
|
||||||
|
|
||||||
private org.slf4j.Logger logger;
|
private org.slf4j.Logger logger;
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
public Slf4jLogger(org.slf4j.Logger logger) {
|
public Slf4jLogger(org.slf4j.Logger logger) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
this.projectId = ProjectBuildLogAppender.getProjectId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void debug(String message) {
|
public void debug(String message) {
|
||||||
|
setMdc();
|
||||||
logger.debug(message);
|
logger.debug(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void debug(String message, Throwable throwable) {
|
public void debug(String message, Throwable throwable) {
|
||||||
|
setMdc();
|
||||||
logger.debug(message, throwable);
|
logger.debug(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,10 +52,12 @@ public class Slf4jLogger implements Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void info(String message) {
|
public void info(String message) {
|
||||||
|
setMdc();
|
||||||
logger.info(message);
|
logger.info(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void info(String message, Throwable throwable) {
|
public void info(String message, Throwable throwable) {
|
||||||
|
setMdc();
|
||||||
logger.info(message, throwable);
|
logger.info(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,10 +66,12 @@ public class Slf4jLogger implements Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void warn(String message) {
|
public void warn(String message) {
|
||||||
|
setMdc();
|
||||||
logger.warn(message);
|
logger.warn(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void warn(String message, Throwable throwable) {
|
public void warn(String message, Throwable throwable) {
|
||||||
|
setMdc();
|
||||||
logger.warn(message, throwable);
|
logger.warn(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,10 +80,12 @@ public class Slf4jLogger implements Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(String message) {
|
public void error(String message) {
|
||||||
|
setMdc();
|
||||||
logger.error(message);
|
logger.error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(String message, Throwable throwable) {
|
public void error(String message, Throwable throwable) {
|
||||||
|
setMdc();
|
||||||
logger.error(message, throwable);
|
logger.error(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,10 +94,12 @@ public class Slf4jLogger implements Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fatalError(String message) {
|
public void fatalError(String message) {
|
||||||
|
setMdc();
|
||||||
logger.error(message);
|
logger.error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fatalError(String message, Throwable throwable) {
|
public void fatalError(String message, Throwable throwable) {
|
||||||
|
setMdc();
|
||||||
logger.error(message, throwable);
|
logger.error(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,4 +129,10 @@ public class Slf4jLogger implements Logger {
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return logger.getName();
|
return logger.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setMdc() {
|
||||||
|
if (projectId != null && ProjectBuildLogAppender.getProjectId() == null) {
|
||||||
|
ProjectBuildLogAppender.setProjectId(projectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,16 @@
|
||||||
package org.apache.maven.cli.logging.impl;
|
package org.apache.maven.cli.logging.impl;
|
||||||
|
|
||||||
import org.apache.maven.cli.logging.BaseSlf4jConfiguration;
|
import org.apache.maven.cli.logging.BaseSlf4jConfiguration;
|
||||||
import org.slf4j.simple.MavenSlf4jSimpleFriend;
|
import org.apache.maven.slf4j.MavenLoggerFactory;
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for slf4j-simple.
|
* Configuration for slf4j-simple.
|
||||||
*
|
*
|
||||||
* @since 3.1.0
|
* @since 3.1.0
|
||||||
*/
|
*/
|
||||||
public class Slf4jSimpleConfiguration extends BaseSlf4jConfiguration {
|
public class MavenSimpleConfiguration extends BaseSlf4jConfiguration {
|
||||||
@Override
|
@Override
|
||||||
public void setRootLoggerLevel(Level level) {
|
public void setRootLoggerLevel(Level level) {
|
||||||
String value;
|
String value;
|
||||||
|
@ -48,7 +50,9 @@ public class Slf4jSimpleConfiguration extends BaseSlf4jConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
// property for root logger level or System.out redirection need to be taken into account
|
ILoggerFactory lf = LoggerFactory.getILoggerFactory();
|
||||||
MavenSlf4jSimpleFriend.init();
|
if (lf instanceof MavenLoggerFactory mlf) {
|
||||||
|
mlf.reconfigure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package org.slf4j.simple;
|
|
||||||
|
|
||||||
import org.slf4j.ILoggerFactory;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility for Maven to access Slf4j-Simple internals through package access.
|
|
||||||
* Use with precaution, since this is not normally intended for production use.
|
|
||||||
*/
|
|
||||||
public class MavenSlf4jSimpleFriend {
|
|
||||||
public static void init() {
|
|
||||||
SimpleLogger.init();
|
|
||||||
ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
|
|
||||||
if (loggerFactory instanceof SimpleLoggerFactory) {
|
|
||||||
((SimpleLoggerFactory) loggerFactory).reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
# key = Slf4j effective logger factory implementation
|
# key = Slf4j effective logger factory implementation
|
||||||
# value = corresponding o.a.m.cli.logging.Slf4jConfiguration class
|
# value = corresponding o.a.m.cli.logging.Slf4jConfiguration class
|
||||||
org.slf4j.impl.SimpleLoggerFactory=org.apache.maven.cli.logging.impl.Slf4jSimpleConfiguration
|
org.slf4j.impl.SimpleLoggerFactory=org.apache.maven.cli.logging.impl.MavenSimpleConfiguration
|
||||||
org.apache.maven.slf4j.MavenLoggerFactory=org.apache.maven.cli.logging.impl.Slf4jSimpleConfiguration
|
org.apache.maven.slf4j.MavenLoggerFactory=org.apache.maven.cli.logging.impl.MavenSimpleConfiguration
|
||||||
org.apache.logging.slf4j.Log4jLoggerFactory=org.apache.maven.cli.logging.impl.Log4j2Configuration
|
org.apache.logging.slf4j.Log4jLoggerFactory=org.apache.maven.cli.logging.impl.Log4j2Configuration
|
||||||
ch.qos.logback.classic.LoggerContext=org.apache.maven.cli.logging.impl.LogbackConfiguration
|
ch.qos.logback.classic.LoggerContext=org.apache.maven.cli.logging.impl.LogbackConfiguration
|
||||||
|
|
|
@ -98,4 +98,8 @@ public class MessageUtils {
|
||||||
public static MessageBuilder builder() {
|
public static MessageBuilder builder() {
|
||||||
return messageBuilderFactory.builder();
|
return messageBuilderFactory.builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Terminal getTerminal() {
|
||||||
|
return terminal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,19 +19,25 @@ under the License.
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven</artifactId>
|
<artifactId>maven</artifactId>
|
||||||
<version>4.0.0-beta-5-SNAPSHOT</version>
|
<version>4.0.0-beta-5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
<artifactId>maven-logging</artifactId>
|
||||||
|
<name>Maven Logging</name>
|
||||||
<name>Maven SLF4J Wrapper</name>
|
<description>Provides the Maven Logging infrastructure</description>
|
||||||
<description>This modules provides an ILoggerFactory interface which avoids a cyclic dependency between maven-embedder and maven-slf4j-provider.</description>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-jline</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
@ -48,4 +54,14 @@ under the License.
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.eclipse.sisu</groupId>
|
||||||
|
<artifactId>sisu-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -16,17 +16,22 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.slf4j;
|
package org.apache.maven.logging.api;
|
||||||
|
|
||||||
/**
|
public interface LogLevelRecorder {
|
||||||
* Utility for Maven to access Slf4j internals through package access.
|
|
||||||
* Use with precaution, since this is not normally intended for production use.
|
enum Level {
|
||||||
*/
|
DEBUG,
|
||||||
public class MavenSlf4jFriend {
|
INFO,
|
||||||
/**
|
WARN,
|
||||||
* Reset Slf4j internal state.
|
ERROR
|
||||||
*/
|
|
||||||
public static void reset() {
|
|
||||||
LoggerFactory.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasReachedMaxLevel();
|
||||||
|
|
||||||
|
Level getMaxLevelReached();
|
||||||
|
|
||||||
|
Level getMaxLevelAllowed();
|
||||||
|
|
||||||
|
void setMaxLevelAllowed(Level level);
|
||||||
}
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import org.apache.maven.logging.api.LogLevelRecorder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for keeping state of whether the threshold of the --fail-on-severity flag has been hit.
|
||||||
|
*/
|
||||||
|
public class DefaultLogLevelRecorder implements LogLevelRecorder {
|
||||||
|
private static final Map<String, Level> ACCEPTED_LEVELS = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
ACCEPTED_LEVELS.put("WARN", Level.WARN);
|
||||||
|
ACCEPTED_LEVELS.put("WARNING", Level.WARN);
|
||||||
|
ACCEPTED_LEVELS.put("ERROR", Level.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Level maxAllowed;
|
||||||
|
private final AtomicReference<Level> maxReached = new AtomicReference<>(Level.DEBUG);
|
||||||
|
|
||||||
|
public DefaultLogLevelRecorder(String threshold) {
|
||||||
|
this(determineThresholdLevel(threshold));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultLogLevelRecorder(Level maxAllowed) {
|
||||||
|
this.maxAllowed = maxAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasReachedMaxLevel() {
|
||||||
|
return maxReached.get().ordinal() > maxAllowed.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Level getMaxLevelReached() {
|
||||||
|
return maxReached.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Level getMaxLevelAllowed() {
|
||||||
|
return maxAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMaxLevelAllowed(Level level) {
|
||||||
|
this.maxAllowed = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Level determineThresholdLevel(String input) {
|
||||||
|
final Level result = ACCEPTED_LEVELS.get(input);
|
||||||
|
if (result == null) {
|
||||||
|
String message = String.format(
|
||||||
|
"%s is not a valid log severity threshold. Valid severities are WARN/WARNING and ERROR.", input);
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void record(org.slf4j.event.Level logLevel) {
|
||||||
|
Level level =
|
||||||
|
switch (logLevel) {
|
||||||
|
case TRACE, DEBUG -> Level.DEBUG;
|
||||||
|
case INFO -> Level.INFO;
|
||||||
|
case WARN -> Level.WARN;
|
||||||
|
case ERROR -> Level.ERROR;
|
||||||
|
};
|
||||||
|
while (true) {
|
||||||
|
Level r = maxReached.get();
|
||||||
|
if (level.ordinal() > r.ordinal()) {
|
||||||
|
if (!maxReached.compareAndSet(r, level)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean metThreshold() {
|
||||||
|
return maxReached.get().ordinal() >= maxAllowed.ordinal();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,474 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
import org.slf4j.event.Level;
|
||||||
|
import org.slf4j.event.LoggingEvent;
|
||||||
|
import org.slf4j.helpers.LegacyAbstractLogger;
|
||||||
|
import org.slf4j.helpers.MessageFormatter;
|
||||||
|
import org.slf4j.helpers.NormalizedParameters;
|
||||||
|
import org.slf4j.spi.LocationAwareLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Simple implementation of {@link Logger} that sends all enabled log messages,
|
||||||
|
* for all defined loggers, to the console ({@code System.err}). The following
|
||||||
|
* system properties are supported to configure the behavior of this logger:
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li><code>org.slf4j.simpleLogger.logFile</code> - The output target which can
|
||||||
|
* be the <em>path</em> to a file, or the special values "System.out" and
|
||||||
|
* "System.err". Default is "System.err".</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.cacheOutputStream</code> - If the output
|
||||||
|
* target is set to "System.out" or "System.err" (see preceding entry), by
|
||||||
|
* default, logs will be output to the latest value referenced by
|
||||||
|
* <code>System.out/err</code> variables. By setting this parameter to true, the
|
||||||
|
* output stream will be cached, i.e. assigned once at initialization time and
|
||||||
|
* re-used independently of the current value referenced by
|
||||||
|
* <code>System.out/err</code>.</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.defaultLogLevel</code> - Default log level
|
||||||
|
* for all instances of SimpleLogger. Must be one of ("trace", "debug", "info",
|
||||||
|
* "warn", "error" or "off"). If not specified, defaults to "info".</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.log.<em>a.b.c</em></code> - Logging detail
|
||||||
|
* level for a SimpleLogger instance named "a.b.c". Right-side value must be one
|
||||||
|
* of "trace", "debug", "info", "warn", "error" or "off". When a SimpleLogger
|
||||||
|
* named "a.b.c" is initialized, its level is assigned from this property. If
|
||||||
|
* unspecified, the level of nearest parent logger will be used, and if none is
|
||||||
|
* set, then the value specified by
|
||||||
|
* <code>org.slf4j.simpleLogger.defaultLogLevel</code> will be used.</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.showDateTime</code> - Set to
|
||||||
|
* <code>true</code> if you want the current date and time to be included in
|
||||||
|
* output messages. Default is <code>false</code></li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.dateTimeFormat</code> - The date and time
|
||||||
|
* format to be used in the output messages. The pattern describing the date and
|
||||||
|
* time format is defined by <a href=
|
||||||
|
* "http://docs.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html">
|
||||||
|
* <code>SimpleDateFormat</code></a>. If the format is not specified or is
|
||||||
|
* invalid, the number of milliseconds since start up will be output.</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.showThreadName</code> -Set to
|
||||||
|
* <code>true</code> if you want to output the current thread name. Defaults to
|
||||||
|
* <code>true</code>.</li>
|
||||||
|
*
|
||||||
|
* <li>(since version 1.7.33 and 2.0.0-alpha6) <code>org.slf4j.simpleLogger.showThreadId</code> -
|
||||||
|
* If you would like to output the current thread id, then set to
|
||||||
|
* <code>true</code>. Defaults to <code>false</code>.</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.showLogName</code> - Set to
|
||||||
|
* <code>true</code> if you want the Logger instance name to be included in
|
||||||
|
* output messages. Defaults to <code>true</code>.</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.showShortLogName</code> - Set to
|
||||||
|
* <code>true</code> if you want the last component of the name to be included
|
||||||
|
* in output messages. Defaults to <code>false</code>.</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.levelInBrackets</code> - Should the level
|
||||||
|
* string be output in brackets? Defaults to <code>false</code>.</li>
|
||||||
|
*
|
||||||
|
* <li><code>org.slf4j.simpleLogger.warnLevelString</code> - The string value
|
||||||
|
* output for the warn level. Defaults to <code>WARN</code>.</li>
|
||||||
|
*
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* In addition to looking for system properties with the names specified above,
|
||||||
|
* this implementation also checks for a class loader resource named
|
||||||
|
* <code>"simplelogger.properties"</code>, and includes any matching definitions
|
||||||
|
* from this resource (if it exists).
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* With no configuration, the default output includes the relative time in
|
||||||
|
* milliseconds, thread name, the level, logger name, and the message followed
|
||||||
|
* by the line separator for the host. In log4j terms it amounts to the "%r [%t]
|
||||||
|
* %level %logger - %m%n" pattern.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Sample output follows.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse order.
|
||||||
|
* 225 [main] INFO examples.SortAlgo - Entered the sort method.
|
||||||
|
* 304 [main] INFO examples.SortAlgo - Dump of integer array:
|
||||||
|
* 317 [main] INFO examples.SortAlgo - Element [0] = 0
|
||||||
|
* 331 [main] INFO examples.SortAlgo - Element [1] = 1
|
||||||
|
* 343 [main] INFO examples.Sort - The next log statement should be an error message.
|
||||||
|
* 346 [main] ERROR examples.SortAlgo - Tried to dump an uninitialized array.
|
||||||
|
* at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58)
|
||||||
|
* at org.log4j.examples.Sort.main(Sort.java:64)
|
||||||
|
* 467 [main] INFO examples.Sort - Exiting main method.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This implementation is heavily inspired by
|
||||||
|
* <a href="http://commons.apache.org/logging/">Apache Commons Logging</a>'s
|
||||||
|
* SimpleLog.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Ceki Gülcü
|
||||||
|
* @author Scott Sanders
|
||||||
|
* @author Rod Waldhoff
|
||||||
|
* @author Robert Burrell Donkin
|
||||||
|
* @author Cédrik LIME
|
||||||
|
*/
|
||||||
|
public class MavenBaseLogger extends LegacyAbstractLogger {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -632788891211436180L;
|
||||||
|
|
||||||
|
private static final long START_TIME = System.currentTimeMillis();
|
||||||
|
|
||||||
|
protected static final int LOG_LEVEL_TRACE = LocationAwareLogger.TRACE_INT;
|
||||||
|
protected static final int LOG_LEVEL_DEBUG = LocationAwareLogger.DEBUG_INT;
|
||||||
|
protected static final int LOG_LEVEL_INFO = LocationAwareLogger.INFO_INT;
|
||||||
|
protected static final int LOG_LEVEL_WARN = LocationAwareLogger.WARN_INT;
|
||||||
|
protected static final int LOG_LEVEL_ERROR = LocationAwareLogger.ERROR_INT;
|
||||||
|
|
||||||
|
static final char SP = ' ';
|
||||||
|
static final String TID_PREFIX = "tid=";
|
||||||
|
|
||||||
|
// The OFF level can only be used in configuration files to disable logging.
|
||||||
|
// It has
|
||||||
|
// no printing method associated with it in o.s.Logger interface.
|
||||||
|
protected static final int LOG_LEVEL_OFF = LOG_LEVEL_ERROR + 10;
|
||||||
|
|
||||||
|
static final SimpleLoggerConfiguration CONFIG_PARAMS = new SimpleLoggerConfiguration();
|
||||||
|
|
||||||
|
private static boolean initialized = false;
|
||||||
|
|
||||||
|
static void lazyInit() {
|
||||||
|
if (initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// external software might be invoking this method directly. Do not rename
|
||||||
|
// or change its semantics.
|
||||||
|
static void init() {
|
||||||
|
CONFIG_PARAMS.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The current log level */
|
||||||
|
protected int currentLogLevel = LOG_LEVEL_INFO;
|
||||||
|
/** The short name of this simple log instance */
|
||||||
|
private transient String shortLogName = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All system properties used by <code>SimpleLogger</code> start with this
|
||||||
|
* prefix
|
||||||
|
*/
|
||||||
|
public static final String SYSTEM_PREFIX = "org.slf4j.simpleLogger.";
|
||||||
|
|
||||||
|
public static final String LOG_KEY_PREFIX = MavenBaseLogger.SYSTEM_PREFIX + "log.";
|
||||||
|
|
||||||
|
public static final String CACHE_OUTPUT_STREAM_STRING_KEY = MavenBaseLogger.SYSTEM_PREFIX + "cacheOutputStream";
|
||||||
|
|
||||||
|
public static final String WARN_LEVEL_STRING_KEY = MavenBaseLogger.SYSTEM_PREFIX + "warnLevelString";
|
||||||
|
|
||||||
|
public static final String LEVEL_IN_BRACKETS_KEY = MavenBaseLogger.SYSTEM_PREFIX + "levelInBrackets";
|
||||||
|
|
||||||
|
public static final String LOG_FILE_KEY = MavenBaseLogger.SYSTEM_PREFIX + "logFile";
|
||||||
|
|
||||||
|
public static final String SHOW_SHORT_LOG_NAME_KEY = MavenBaseLogger.SYSTEM_PREFIX + "showShortLogName";
|
||||||
|
|
||||||
|
public static final String SHOW_LOG_NAME_KEY = MavenBaseLogger.SYSTEM_PREFIX + "showLogName";
|
||||||
|
|
||||||
|
public static final String SHOW_THREAD_NAME_KEY = MavenBaseLogger.SYSTEM_PREFIX + "showThreadName";
|
||||||
|
|
||||||
|
public static final String SHOW_THREAD_ID_KEY = MavenBaseLogger.SYSTEM_PREFIX + "showThreadId";
|
||||||
|
|
||||||
|
public static final String DATE_TIME_FORMAT_KEY = MavenBaseLogger.SYSTEM_PREFIX + "dateTimeFormat";
|
||||||
|
|
||||||
|
public static final String SHOW_DATE_TIME_KEY = MavenBaseLogger.SYSTEM_PREFIX + "showDateTime";
|
||||||
|
|
||||||
|
public static final String DEFAULT_LOG_LEVEL_KEY = MavenBaseLogger.SYSTEM_PREFIX + "defaultLogLevel";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected access allows only {@link MavenLoggerFactory} and also derived classes to instantiate
|
||||||
|
* MavenLoggerFactory instances.
|
||||||
|
*/
|
||||||
|
protected MavenBaseLogger(String name) {
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
String levelString = recursivelyComputeLevelString();
|
||||||
|
if (levelString != null) {
|
||||||
|
this.currentLogLevel = SimpleLoggerConfiguration.stringToLevel(levelString);
|
||||||
|
} else {
|
||||||
|
this.currentLogLevel = CONFIG_PARAMS.defaultLogLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String recursivelyComputeLevelString() {
|
||||||
|
String tempName = name;
|
||||||
|
String levelString = null;
|
||||||
|
int indexOfLastDot = tempName.length();
|
||||||
|
while ((levelString == null) && (indexOfLastDot > -1)) {
|
||||||
|
tempName = tempName.substring(0, indexOfLastDot);
|
||||||
|
levelString = CONFIG_PARAMS.getStringProperty(MavenBaseLogger.LOG_KEY_PREFIX + tempName, null);
|
||||||
|
indexOfLastDot = String.valueOf(tempName).lastIndexOf(".");
|
||||||
|
}
|
||||||
|
return levelString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To avoid intermingling of log messages and associated stack traces, the two
|
||||||
|
* operations are done in a synchronized block.
|
||||||
|
*
|
||||||
|
* @param buf
|
||||||
|
* @param t
|
||||||
|
*/
|
||||||
|
protected void write(StringBuilder buf, Throwable t) {
|
||||||
|
PrintStream targetStream = CONFIG_PARAMS.outputChoice.getTargetPrintStream();
|
||||||
|
|
||||||
|
synchronized (CONFIG_PARAMS) {
|
||||||
|
targetStream.println(buf.toString());
|
||||||
|
writeThrowable(t, targetStream);
|
||||||
|
targetStream.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeThrowable(Throwable t, PrintStream targetStream) {
|
||||||
|
if (t != null) {
|
||||||
|
t.printStackTrace(targetStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getFormattedDate() {
|
||||||
|
Date now = new Date();
|
||||||
|
String dateText;
|
||||||
|
synchronized (CONFIG_PARAMS.dateFormatter) {
|
||||||
|
dateText = CONFIG_PARAMS.dateFormatter.format(now);
|
||||||
|
}
|
||||||
|
return dateText;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String computeShortName() {
|
||||||
|
return name.substring(name.lastIndexOf(".") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * For formatted messages, first substitute arguments and then log.
|
||||||
|
// *
|
||||||
|
// * @param level
|
||||||
|
// * @param format
|
||||||
|
// * @param arg1
|
||||||
|
// * @param arg2
|
||||||
|
// */
|
||||||
|
// private void formatAndLog(int level, String format, Object arg1, Object arg2) {
|
||||||
|
// if (!isLevelEnabled(level)) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// FormattingTuple tp = MessageFormatter.format(format, arg1, arg2);
|
||||||
|
// log(level, tp.getMessage(), tp.getThrowable());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * For formatted messages, first substitute arguments and then log.
|
||||||
|
// *
|
||||||
|
// * @param level
|
||||||
|
// * @param format
|
||||||
|
// * @param arguments
|
||||||
|
// * a list of 3 ore more arguments
|
||||||
|
// */
|
||||||
|
// private void formatAndLog(int level, String format, Object... arguments) {
|
||||||
|
// if (!isLevelEnabled(level)) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// FormattingTuple tp = MessageFormatter.arrayFormat(format, arguments);
|
||||||
|
// log(level, tp.getMessage(), tp.getThrowable());
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the given log level currently enabled?
|
||||||
|
*
|
||||||
|
* @param logLevel is this level enabled?
|
||||||
|
* @return whether the logger is enabled for the given level
|
||||||
|
*/
|
||||||
|
protected boolean isLevelEnabled(int logLevel) {
|
||||||
|
// log level are numerically ordered so can use simple numeric
|
||||||
|
// comparison
|
||||||
|
return (logLevel >= currentLogLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Are {@code trace} messages currently enabled? */
|
||||||
|
public boolean isTraceEnabled() {
|
||||||
|
return isLevelEnabled(LOG_LEVEL_TRACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Are {@code debug} messages currently enabled? */
|
||||||
|
public boolean isDebugEnabled() {
|
||||||
|
return isLevelEnabled(LOG_LEVEL_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Are {@code info} messages currently enabled? */
|
||||||
|
public boolean isInfoEnabled() {
|
||||||
|
return isLevelEnabled(LOG_LEVEL_INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Are {@code warn} messages currently enabled? */
|
||||||
|
public boolean isWarnEnabled() {
|
||||||
|
return isLevelEnabled(LOG_LEVEL_WARN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Are {@code error} messages currently enabled? */
|
||||||
|
public boolean isErrorEnabled() {
|
||||||
|
return isLevelEnabled(LOG_LEVEL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SimpleLogger's implementation of
|
||||||
|
* {@link org.slf4j.helpers.AbstractLogger#handleNormalizedLoggingCall(Level, Marker, String, Object[], Throwable) AbstractLogger#handleNormalizedLoggingCall}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param level the SLF4J level for this event
|
||||||
|
* @param marker The marker to be used for this event, may be null.
|
||||||
|
* @param messagePattern The message pattern which will be parsed and formatted
|
||||||
|
* @param arguments the array of arguments to be formatted, may be null
|
||||||
|
* @param throwable The exception whose stack trace should be logged, may be null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void handleNormalizedLoggingCall(
|
||||||
|
Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
|
||||||
|
|
||||||
|
List<Marker> markers = null;
|
||||||
|
|
||||||
|
if (marker != null) {
|
||||||
|
markers = new ArrayList<>();
|
||||||
|
markers.add(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
innerHandleNormalizedLoggingCall(level, markers, messagePattern, arguments, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void innerHandleNormalizedLoggingCall(
|
||||||
|
Level level, List<Marker> markers, String messagePattern, Object[] arguments, Throwable t) {
|
||||||
|
|
||||||
|
StringBuilder buf = new StringBuilder(32);
|
||||||
|
|
||||||
|
// Append date-time if so configured
|
||||||
|
if (CONFIG_PARAMS.showDateTime) {
|
||||||
|
if (CONFIG_PARAMS.dateFormatter != null) {
|
||||||
|
buf.append(getFormattedDate());
|
||||||
|
buf.append(SP);
|
||||||
|
} else {
|
||||||
|
buf.append(System.currentTimeMillis() - START_TIME);
|
||||||
|
buf.append(SP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append current thread name if so configured
|
||||||
|
if (CONFIG_PARAMS.showThreadName) {
|
||||||
|
buf.append('[');
|
||||||
|
buf.append(Thread.currentThread().getName());
|
||||||
|
buf.append("] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CONFIG_PARAMS.showThreadId) {
|
||||||
|
buf.append(TID_PREFIX);
|
||||||
|
buf.append(Thread.currentThread().getId());
|
||||||
|
buf.append(SP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CONFIG_PARAMS.levelInBrackets) {
|
||||||
|
buf.append('[');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append a readable representation of the log level
|
||||||
|
String levelStr = renderLevel(level.toInt());
|
||||||
|
buf.append(levelStr);
|
||||||
|
if (CONFIG_PARAMS.levelInBrackets) {
|
||||||
|
buf.append(']');
|
||||||
|
}
|
||||||
|
buf.append(SP);
|
||||||
|
|
||||||
|
// Append the name of the log instance if so configured
|
||||||
|
if (CONFIG_PARAMS.showShortLogName) {
|
||||||
|
if (shortLogName == null) {
|
||||||
|
shortLogName = computeShortName();
|
||||||
|
}
|
||||||
|
buf.append(shortLogName).append(" - ");
|
||||||
|
} else if (CONFIG_PARAMS.showLogName) {
|
||||||
|
buf.append(name).append(" - ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markers != null) {
|
||||||
|
buf.append(SP);
|
||||||
|
for (Marker marker : markers) {
|
||||||
|
buf.append(marker.getName()).append(SP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String formattedMessage = MessageFormatter.basicArrayFormat(messagePattern, arguments);
|
||||||
|
|
||||||
|
// Append the message
|
||||||
|
buf.append(formattedMessage);
|
||||||
|
|
||||||
|
write(buf, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String renderLevel(int levelInt) {
|
||||||
|
switch (levelInt) {
|
||||||
|
case LOG_LEVEL_TRACE:
|
||||||
|
return "TRACE";
|
||||||
|
case LOG_LEVEL_DEBUG:
|
||||||
|
return ("DEBUG");
|
||||||
|
case LOG_LEVEL_INFO:
|
||||||
|
return "INFO";
|
||||||
|
case LOG_LEVEL_WARN:
|
||||||
|
return "WARN";
|
||||||
|
case LOG_LEVEL_ERROR:
|
||||||
|
return "ERROR";
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unrecognized level [" + levelInt + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(LoggingEvent event) {
|
||||||
|
int levelInt = event.getLevel().toInt();
|
||||||
|
|
||||||
|
if (!isLevelEnabled(levelInt)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NormalizedParameters np = NormalizedParameters.normalize(event);
|
||||||
|
|
||||||
|
innerHandleNormalizedLoggingCall(
|
||||||
|
event.getLevel(), event.getMarkers(), np.getMessage(), np.getArguments(), event.getThrowable());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getFullyQualifiedCallerName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.slf4j;
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,9 +25,9 @@ import org.slf4j.event.Level;
|
||||||
* Currently only support WARN and ERROR states, since it's been used for the --fail-on-severity flag.
|
* Currently only support WARN and ERROR states, since it's been used for the --fail-on-severity flag.
|
||||||
*/
|
*/
|
||||||
public class MavenFailOnSeverityLogger extends MavenSimpleLogger {
|
public class MavenFailOnSeverityLogger extends MavenSimpleLogger {
|
||||||
private final LogLevelRecorder logLevelRecorder;
|
private final DefaultLogLevelRecorder logLevelRecorder;
|
||||||
|
|
||||||
MavenFailOnSeverityLogger(String name, LogLevelRecorder logLevelRecorder) {
|
MavenFailOnSeverityLogger(String name, DefaultLogLevelRecorder logLevelRecorder) {
|
||||||
super(name);
|
super(name);
|
||||||
this.logLevelRecorder = logLevelRecorder;
|
this.logLevelRecorder = logLevelRecorder;
|
||||||
}
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LogFactory for Maven which can create a simple logger or one which, if set, fails the build on a severity threshold.
|
||||||
|
*/
|
||||||
|
public class MavenLoggerFactory implements org.apache.maven.logging.api.LogLevelRecorder, ILoggerFactory {
|
||||||
|
DefaultLogLevelRecorder logLevelRecorder = null;
|
||||||
|
final ConcurrentMap<String, Logger> loggerMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public MavenLoggerFactory() {
|
||||||
|
MavenSimpleLogger.lazyInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasReachedMaxLevel() {
|
||||||
|
return logLevelRecorder != null && logLevelRecorder.metThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Level getMaxLevelReached() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Level getMaxLevelAllowed() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMaxLevelAllowed(Level level) {
|
||||||
|
this.logLevelRecorder = new DefaultLogLevelRecorder(level.name());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return an appropriate {@link Logger} instance by name.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Logger getLogger(String name) {
|
||||||
|
return loggerMap.computeIfAbsent(name, this::getNewLoggingInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Logger getNewLoggingInstance(String name) {
|
||||||
|
if (logLevelRecorder == null) {
|
||||||
|
return new MavenSimpleLogger(name);
|
||||||
|
} else {
|
||||||
|
return new MavenFailOnSeverityLogger(name, logLevelRecorder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reconfigure() {
|
||||||
|
SimpleLoggerConfiguration config = MavenSimpleLogger.CONFIG_PARAMS;
|
||||||
|
config.init();
|
||||||
|
loggerMap.values().forEach(l -> {
|
||||||
|
if (l instanceof MavenSimpleLogger msl) {
|
||||||
|
msl.configure(config.defaultLogLevel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,10 +18,12 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.slf4j;
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
|
||||||
import org.slf4j.ILoggerFactory;
|
import org.slf4j.ILoggerFactory;
|
||||||
import org.slf4j.IMarkerFactory;
|
import org.slf4j.IMarkerFactory;
|
||||||
|
import org.slf4j.helpers.BasicMDCAdapter;
|
||||||
import org.slf4j.helpers.BasicMarkerFactory;
|
import org.slf4j.helpers.BasicMarkerFactory;
|
||||||
import org.slf4j.helpers.NOPMDCAdapter;
|
|
||||||
import org.slf4j.spi.MDCAdapter;
|
import org.slf4j.spi.MDCAdapter;
|
||||||
import org.slf4j.spi.SLF4JServiceProvider;
|
import org.slf4j.spi.SLF4JServiceProvider;
|
||||||
|
|
||||||
|
@ -35,9 +37,13 @@ public class MavenServiceProvider implements SLF4JServiceProvider {
|
||||||
@SuppressWarnings({"checkstyle:StaticVariableName", "checkstyle:VisibilityModifier"})
|
@SuppressWarnings({"checkstyle:StaticVariableName", "checkstyle:VisibilityModifier"})
|
||||||
public static String REQUESTED_API_VERSION = "2.0.99"; // !final
|
public static String REQUESTED_API_VERSION = "2.0.99"; // !final
|
||||||
|
|
||||||
private MavenLoggerFactory loggerFactory = new MavenLoggerFactory();
|
private MavenLoggerFactory loggerFactory = loadMavenLoggerFactory();
|
||||||
private IMarkerFactory markerFactory = new BasicMarkerFactory();
|
private IMarkerFactory markerFactory = new BasicMarkerFactory();
|
||||||
private MDCAdapter mdcAdapter = new NOPMDCAdapter();
|
private MDCAdapter mdcAdapter = new BasicMDCAdapter();
|
||||||
|
|
||||||
|
protected MavenLoggerFactory loadMavenLoggerFactory() {
|
||||||
|
return ServiceLoader.load(MavenLoggerFactory.class).findFirst().orElseGet(MavenLoggerFactory::new);
|
||||||
|
}
|
||||||
|
|
||||||
public ILoggerFactory getLoggerFactory() {
|
public ILoggerFactory getLoggerFactory() {
|
||||||
return loggerFactory;
|
return loggerFactory;
|
|
@ -22,7 +22,6 @@ import java.io.PrintStream;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.apache.maven.api.services.MessageBuilder;
|
import org.apache.maven.api.services.MessageBuilder;
|
||||||
import org.slf4j.simple.ExtSimpleLogger;
|
|
||||||
|
|
||||||
import static org.apache.maven.jline.MessageUtils.builder;
|
import static org.apache.maven.jline.MessageUtils.builder;
|
||||||
|
|
||||||
|
@ -32,7 +31,7 @@ import static org.apache.maven.jline.MessageUtils.builder;
|
||||||
*
|
*
|
||||||
* @since 3.5.0
|
* @since 3.5.0
|
||||||
*/
|
*/
|
||||||
public class MavenSimpleLogger extends ExtSimpleLogger {
|
public class MavenSimpleLogger extends MavenBaseLogger {
|
||||||
|
|
||||||
private String traceRenderedLevel;
|
private String traceRenderedLevel;
|
||||||
private String debugRenderedLevel;
|
private String debugRenderedLevel;
|
||||||
|
@ -74,18 +73,24 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void write(StringBuilder buf, Throwable t) {
|
||||||
protected void doWrite(StringBuilder buf, Throwable t) {
|
|
||||||
Consumer<String> sink = logSink;
|
Consumer<String> sink = logSink;
|
||||||
if (sink != null) {
|
if (sink != null) {
|
||||||
sink.accept(buf.toString());
|
sink.accept(buf.toString());
|
||||||
|
if (t != null) {
|
||||||
|
writeThrowable(t, sink);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
super.doWrite(buf, t);
|
super.write(buf, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeThrowable(Throwable t, PrintStream stream) {
|
protected void writeThrowable(Throwable t, PrintStream stream) {
|
||||||
|
writeThrowable(t, stream::println);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeThrowable(Throwable t, Consumer<String> stream) {
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -93,12 +98,12 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
||||||
if (t.getMessage() != null) {
|
if (t.getMessage() != null) {
|
||||||
builder.a(": ").failure(t.getMessage());
|
builder.a(": ").failure(t.getMessage());
|
||||||
}
|
}
|
||||||
stream.println(builder);
|
stream.accept(builder.toString());
|
||||||
|
|
||||||
printStackTrace(t, stream, "");
|
printStackTrace(t, stream, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printStackTrace(Throwable t, PrintStream stream, String prefix) {
|
protected void printStackTrace(Throwable t, Consumer<String> stream, String prefix) {
|
||||||
MessageBuilder builder = builder();
|
MessageBuilder builder = builder();
|
||||||
for (StackTraceElement e : t.getStackTrace()) {
|
for (StackTraceElement e : t.getStackTrace()) {
|
||||||
builder.a(prefix);
|
builder.a(prefix);
|
||||||
|
@ -111,7 +116,7 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
||||||
builder.a("(");
|
builder.a("(");
|
||||||
builder.strong(getLocation(e));
|
builder.strong(getLocation(e));
|
||||||
builder.a(")");
|
builder.a(")");
|
||||||
stream.println(builder);
|
stream.accept(builder.toString());
|
||||||
builder.setLength(0);
|
builder.setLength(0);
|
||||||
}
|
}
|
||||||
for (Throwable se : t.getSuppressed()) {
|
for (Throwable se : t.getSuppressed()) {
|
||||||
|
@ -123,13 +128,13 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeThrowable(Throwable t, PrintStream stream, String caption, String prefix) {
|
protected void writeThrowable(Throwable t, Consumer<String> stream, String caption, String prefix) {
|
||||||
MessageBuilder builder =
|
MessageBuilder builder =
|
||||||
builder().a(prefix).strong(caption).a(": ").a(t.getClass().getName());
|
builder().a(prefix).strong(caption).a(": ").a(t.getClass().getName());
|
||||||
if (t.getMessage() != null) {
|
if (t.getMessage() != null) {
|
||||||
builder.a(": ").failure(t.getMessage());
|
builder.a(": ").failure(t.getMessage());
|
||||||
}
|
}
|
||||||
stream.println(builder);
|
stream.accept(builder.toString());
|
||||||
|
|
||||||
printStackTrace(t, stream, prefix);
|
printStackTrace(t, stream, prefix);
|
||||||
}
|
}
|
||||||
|
@ -147,4 +152,17 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
||||||
return e.getFileName();
|
return e.getFileName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void configure(int defaultLogLevel) {
|
||||||
|
String levelString = recursivelyComputeLevelString();
|
||||||
|
if (levelString != null) {
|
||||||
|
this.currentLogLevel = SimpleLoggerConfiguration.stringToLevel(levelString);
|
||||||
|
} else {
|
||||||
|
this.currentLogLevel = defaultLogLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogLevel(int logLevel) {
|
||||||
|
this.currentLogLevel = logLevel;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class encapsulates the user's choice of output target.
|
||||||
|
*
|
||||||
|
* @author Ceki Gülcü
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class OutputChoice {
|
||||||
|
|
||||||
|
enum OutputChoiceType {
|
||||||
|
SYS_OUT,
|
||||||
|
CACHED_SYS_OUT,
|
||||||
|
SYS_ERR,
|
||||||
|
CACHED_SYS_ERR,
|
||||||
|
FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
final OutputChoiceType outputChoiceType;
|
||||||
|
final PrintStream targetPrintStream;
|
||||||
|
|
||||||
|
OutputChoice(OutputChoiceType outputChoiceType) {
|
||||||
|
if (outputChoiceType == OutputChoiceType.FILE) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
this.outputChoiceType = outputChoiceType;
|
||||||
|
if (outputChoiceType == OutputChoiceType.CACHED_SYS_OUT) {
|
||||||
|
this.targetPrintStream = System.out;
|
||||||
|
} else if (outputChoiceType == OutputChoiceType.CACHED_SYS_ERR) {
|
||||||
|
this.targetPrintStream = System.err;
|
||||||
|
} else {
|
||||||
|
this.targetPrintStream = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputChoice(PrintStream printStream) {
|
||||||
|
this.outputChoiceType = OutputChoiceType.FILE;
|
||||||
|
this.targetPrintStream = printStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintStream getTargetPrintStream() {
|
||||||
|
switch (outputChoiceType) {
|
||||||
|
case SYS_OUT:
|
||||||
|
return System.out;
|
||||||
|
case SYS_ERR:
|
||||||
|
return System.err;
|
||||||
|
case CACHED_SYS_ERR:
|
||||||
|
case CACHED_SYS_OUT:
|
||||||
|
case FILE:
|
||||||
|
return targetPrintStream;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.apache.maven.slf4j.OutputChoice.OutputChoiceType;
|
||||||
|
import org.slf4j.helpers.Reporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds configuration values for {@link MavenBaseLogger}. The
|
||||||
|
* values are computed at runtime. See {@link MavenBaseLogger} documentation for
|
||||||
|
* more information.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Ceki Gülcü
|
||||||
|
* @author Scott Sanders
|
||||||
|
* @author Rod Waldhoff
|
||||||
|
* @author Robert Burrell Donkin
|
||||||
|
* @author Cédrik LIME
|
||||||
|
*
|
||||||
|
* @since 1.7.25
|
||||||
|
*/
|
||||||
|
public class SimpleLoggerConfiguration {
|
||||||
|
|
||||||
|
private static final String CONFIGURATION_FILE = "simplelogger.properties";
|
||||||
|
|
||||||
|
static final int DEFAULT_LOG_LEVEL_DEFAULT = MavenBaseLogger.LOG_LEVEL_INFO;
|
||||||
|
int defaultLogLevel = DEFAULT_LOG_LEVEL_DEFAULT;
|
||||||
|
|
||||||
|
private static final boolean SHOW_DATE_TIME_DEFAULT = false;
|
||||||
|
boolean showDateTime = SHOW_DATE_TIME_DEFAULT;
|
||||||
|
|
||||||
|
private static final String DATE_TIME_FORMAT_STR_DEFAULT = null;
|
||||||
|
private static String dateTimeFormatStr = DATE_TIME_FORMAT_STR_DEFAULT;
|
||||||
|
|
||||||
|
DateFormat dateFormatter = null;
|
||||||
|
|
||||||
|
private static final boolean SHOW_THREAD_NAME_DEFAULT = true;
|
||||||
|
boolean showThreadName = SHOW_THREAD_NAME_DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://jira.qos.ch/browse/SLF4J-499
|
||||||
|
* @since 1.7.33 and 2.0.0-alpha6
|
||||||
|
*/
|
||||||
|
private static final boolean SHOW_THREAD_ID_DEFAULT = false;
|
||||||
|
|
||||||
|
boolean showThreadId = SHOW_THREAD_ID_DEFAULT;
|
||||||
|
|
||||||
|
static final boolean SHOW_LOG_NAME_DEFAULT = true;
|
||||||
|
boolean showLogName = SHOW_LOG_NAME_DEFAULT;
|
||||||
|
|
||||||
|
private static final boolean SHOW_SHORT_LOG_NAME_DEFAULT = false;
|
||||||
|
boolean showShortLogName = SHOW_SHORT_LOG_NAME_DEFAULT;
|
||||||
|
|
||||||
|
private static final boolean LEVEL_IN_BRACKETS_DEFAULT = false;
|
||||||
|
boolean levelInBrackets = LEVEL_IN_BRACKETS_DEFAULT;
|
||||||
|
|
||||||
|
private static final String LOG_FILE_DEFAULT = "System.err";
|
||||||
|
private String logFile = LOG_FILE_DEFAULT;
|
||||||
|
OutputChoice outputChoice = null;
|
||||||
|
|
||||||
|
private static final boolean CACHE_OUTPUT_STREAM_DEFAULT = false;
|
||||||
|
private boolean cacheOutputStream = CACHE_OUTPUT_STREAM_DEFAULT;
|
||||||
|
|
||||||
|
private static final String WARN_LEVELS_STRING_DEFAULT = "WARN";
|
||||||
|
String warnLevelString = WARN_LEVELS_STRING_DEFAULT;
|
||||||
|
|
||||||
|
private final Properties properties = new Properties();
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
loadProperties();
|
||||||
|
|
||||||
|
String defaultLogLevelString = getStringProperty(MavenBaseLogger.DEFAULT_LOG_LEVEL_KEY, null);
|
||||||
|
if (defaultLogLevelString != null) {
|
||||||
|
defaultLogLevel = stringToLevel(defaultLogLevelString);
|
||||||
|
}
|
||||||
|
|
||||||
|
showLogName =
|
||||||
|
getBooleanProperty(MavenBaseLogger.SHOW_LOG_NAME_KEY, SimpleLoggerConfiguration.SHOW_LOG_NAME_DEFAULT);
|
||||||
|
showShortLogName = getBooleanProperty(MavenBaseLogger.SHOW_SHORT_LOG_NAME_KEY, SHOW_SHORT_LOG_NAME_DEFAULT);
|
||||||
|
showDateTime = getBooleanProperty(MavenBaseLogger.SHOW_DATE_TIME_KEY, SHOW_DATE_TIME_DEFAULT);
|
||||||
|
showThreadName = getBooleanProperty(MavenBaseLogger.SHOW_THREAD_NAME_KEY, SHOW_THREAD_NAME_DEFAULT);
|
||||||
|
showThreadId = getBooleanProperty(MavenBaseLogger.SHOW_THREAD_ID_KEY, SHOW_THREAD_ID_DEFAULT);
|
||||||
|
dateTimeFormatStr = getStringProperty(MavenBaseLogger.DATE_TIME_FORMAT_KEY, DATE_TIME_FORMAT_STR_DEFAULT);
|
||||||
|
levelInBrackets = getBooleanProperty(MavenBaseLogger.LEVEL_IN_BRACKETS_KEY, LEVEL_IN_BRACKETS_DEFAULT);
|
||||||
|
warnLevelString = getStringProperty(MavenBaseLogger.WARN_LEVEL_STRING_KEY, WARN_LEVELS_STRING_DEFAULT);
|
||||||
|
|
||||||
|
logFile = getStringProperty(MavenBaseLogger.LOG_FILE_KEY, logFile);
|
||||||
|
|
||||||
|
cacheOutputStream =
|
||||||
|
getBooleanProperty(MavenBaseLogger.CACHE_OUTPUT_STREAM_STRING_KEY, CACHE_OUTPUT_STREAM_DEFAULT);
|
||||||
|
outputChoice = computeOutputChoice(logFile, cacheOutputStream);
|
||||||
|
|
||||||
|
if (dateTimeFormatStr != null) {
|
||||||
|
try {
|
||||||
|
dateFormatter = new SimpleDateFormat(dateTimeFormatStr);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Reporter.error("Bad date format in " + CONFIGURATION_FILE + "; will output relative time", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadProperties() {
|
||||||
|
// Add props from the resource simplelogger.properties
|
||||||
|
ClassLoader threadCL = Thread.currentThread().getContextClassLoader();
|
||||||
|
ClassLoader toUseCL = (threadCL != null ? threadCL : ClassLoader.getSystemClassLoader());
|
||||||
|
try (InputStream in = toUseCL.getResourceAsStream(CONFIGURATION_FILE)) {
|
||||||
|
if (in != null) {
|
||||||
|
properties.load(in);
|
||||||
|
}
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getStringProperty(String name, String defaultValue) {
|
||||||
|
String prop = getStringProperty(name);
|
||||||
|
return (prop == null) ? defaultValue : prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean getBooleanProperty(String name, boolean defaultValue) {
|
||||||
|
String prop = getStringProperty(name);
|
||||||
|
return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getStringProperty(String name) {
|
||||||
|
String prop = null;
|
||||||
|
try {
|
||||||
|
prop = System.getProperty(name);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
return (prop == null) ? properties.getProperty(name) : prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stringToLevel(String levelStr) {
|
||||||
|
if ("trace".equalsIgnoreCase(levelStr)) {
|
||||||
|
return MavenBaseLogger.LOG_LEVEL_TRACE;
|
||||||
|
} else if ("debug".equalsIgnoreCase(levelStr)) {
|
||||||
|
return MavenBaseLogger.LOG_LEVEL_DEBUG;
|
||||||
|
} else if ("info".equalsIgnoreCase(levelStr)) {
|
||||||
|
return MavenBaseLogger.LOG_LEVEL_INFO;
|
||||||
|
} else if ("warn".equalsIgnoreCase(levelStr)) {
|
||||||
|
return MavenBaseLogger.LOG_LEVEL_WARN;
|
||||||
|
} else if ("error".equalsIgnoreCase(levelStr)) {
|
||||||
|
return MavenBaseLogger.LOG_LEVEL_ERROR;
|
||||||
|
} else if ("off".equalsIgnoreCase(levelStr)) {
|
||||||
|
return MavenBaseLogger.LOG_LEVEL_OFF;
|
||||||
|
}
|
||||||
|
// assume INFO by default
|
||||||
|
return MavenBaseLogger.LOG_LEVEL_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OutputChoice computeOutputChoice(String logFile, boolean cacheOutputStream) {
|
||||||
|
if ("System.err".equalsIgnoreCase(logFile)) {
|
||||||
|
return new OutputChoice(cacheOutputStream ? OutputChoiceType.CACHED_SYS_ERR : OutputChoiceType.SYS_ERR);
|
||||||
|
} else if ("System.out".equalsIgnoreCase(logFile)) {
|
||||||
|
return new OutputChoice(cacheOutputStream ? OutputChoiceType.CACHED_SYS_OUT : OutputChoiceType.SYS_OUT);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
FileOutputStream fos = new FileOutputStream(logFile);
|
||||||
|
PrintStream printStream = new PrintStream(fos);
|
||||||
|
return new OutputChoice(printStream);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Reporter.error("Could not open [" + logFile + "]. Defaulting to System.err", e);
|
||||||
|
return new OutputChoice(OutputChoiceType.SYS_ERR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.logwrapper;
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
|
@ -29,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
class LogLevelRecorderTest {
|
class LogLevelRecorderTest {
|
||||||
@Test
|
@Test
|
||||||
void createsLogLevelRecorder() {
|
void createsLogLevelRecorder() {
|
||||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder("WARN");
|
DefaultLogLevelRecorder logLevelRecorder = new DefaultLogLevelRecorder("WARN");
|
||||||
logLevelRecorder.record(Level.ERROR);
|
logLevelRecorder.record(Level.ERROR);
|
||||||
|
|
||||||
assertTrue(logLevelRecorder.metThreshold());
|
assertTrue(logLevelRecorder.metThreshold());
|
||||||
|
@ -37,12 +37,12 @@ class LogLevelRecorderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void failsOnLowerThanWarn() {
|
void failsOnLowerThanWarn() {
|
||||||
assertThrows(IllegalArgumentException.class, () -> new LogLevelRecorder("INFO"));
|
assertThrows(IllegalArgumentException.class, () -> new DefaultLogLevelRecorder("INFO"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createsLogLevelRecorderWithWarning() {
|
void createsLogLevelRecorderWithWarning() {
|
||||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder("WARNING");
|
DefaultLogLevelRecorder logLevelRecorder = new DefaultLogLevelRecorder("WARNING");
|
||||||
logLevelRecorder.record(Level.ERROR);
|
logLevelRecorder.record(Level.ERROR);
|
||||||
|
|
||||||
assertTrue(logLevelRecorder.metThreshold());
|
assertTrue(logLevelRecorder.metThreshold());
|
||||||
|
@ -50,7 +50,7 @@ class LogLevelRecorderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void failsOnUnknownLogLevel() {
|
void failsOnUnknownLogLevel() {
|
||||||
Throwable thrown = assertThrows(IllegalArgumentException.class, () -> new LogLevelRecorder("SEVERE"));
|
Throwable thrown = assertThrows(IllegalArgumentException.class, () -> new DefaultLogLevelRecorder("SEVERE"));
|
||||||
String message = thrown.getMessage();
|
String message = thrown.getMessage();
|
||||||
assertThat(message, containsString("SEVERE is not a valid log severity threshold"));
|
assertThat(message, containsString("SEVERE is not a valid log severity threshold"));
|
||||||
assertThat(message, containsString("WARN"));
|
assertThat(message, containsString("WARN"));
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.slf4j;
|
package org.apache.maven.slf4j;
|
||||||
|
|
||||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
@ -28,7 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
class MavenLoggerFactoryTest {
|
class MavenLoggerFactoryTest {
|
||||||
|
@ -58,31 +56,18 @@ class MavenLoggerFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
void reportsWhenFailOnSeverityThresholdHasBeenHit() {
|
void reportsWhenFailOnSeverityThresholdHasBeenHit() {
|
||||||
MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
|
MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
|
||||||
mavenLoggerFactory.setLogLevelRecorder(new LogLevelRecorder("ERROR"));
|
mavenLoggerFactory.logLevelRecorder = new DefaultLogLevelRecorder("ERROR");
|
||||||
|
|
||||||
assertTrue(mavenLoggerFactory.getLogLevelRecorder().isPresent());
|
|
||||||
LogLevelRecorder logLevelRecorder =
|
|
||||||
mavenLoggerFactory.getLogLevelRecorder().get();
|
|
||||||
|
|
||||||
MavenFailOnSeverityLogger logger = (MavenFailOnSeverityLogger) mavenLoggerFactory.getLogger("Test");
|
MavenFailOnSeverityLogger logger = (MavenFailOnSeverityLogger) mavenLoggerFactory.getLogger("Test");
|
||||||
assertFalse(logLevelRecorder.metThreshold());
|
assertFalse(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||||
|
|
||||||
logger.warn("This should not hit the fail threshold");
|
logger.warn("This should not hit the fail threshold");
|
||||||
assertFalse(logLevelRecorder.metThreshold());
|
assertFalse(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||||
|
|
||||||
logger.error("This should hit the fail threshold");
|
logger.error("This should hit the fail threshold");
|
||||||
assertTrue(logLevelRecorder.metThreshold());
|
assertTrue(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||||
|
|
||||||
logger.warn("This should not reset the fail threshold");
|
logger.warn("This should not reset the fail threshold");
|
||||||
assertTrue(logLevelRecorder.metThreshold());
|
assertTrue(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void failOnSeverityThresholdCanOnlyBeSetOnce() {
|
|
||||||
MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
|
|
||||||
mavenLoggerFactory.setLogLevelRecorder(new LogLevelRecorder("WARN"));
|
|
||||||
assertThrows(
|
|
||||||
IllegalStateException.class,
|
|
||||||
() -> mavenLoggerFactory.setLogLevelRecorder(new LogLevelRecorder("ERROR")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -49,7 +49,7 @@ class MavenSimpleLoggerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void includesCauseAndSuppressedExceptionsWhenWritingThrowables(TestInfo testInfo) throws Exception {
|
void includesCauseAndSuppressedExceptionsWhenWritingThrowables(TestInfo testInfo) {
|
||||||
Exception causeOfSuppressed = new NoSuchElementException("cause of suppressed");
|
Exception causeOfSuppressed = new NoSuchElementException("cause of suppressed");
|
||||||
Exception suppressed = new IllegalStateException("suppressed", causeOfSuppressed);
|
Exception suppressed = new IllegalStateException("suppressed", causeOfSuppressed);
|
||||||
suppressed.addSuppressed(new IllegalArgumentException(
|
suppressed.addSuppressed(new IllegalArgumentException(
|
||||||
|
@ -62,7 +62,7 @@ class MavenSimpleLoggerTest {
|
||||||
|
|
||||||
new MavenSimpleLogger("logger").writeThrowable(throwable, new PrintStream(output));
|
new MavenSimpleLogger("logger").writeThrowable(throwable, new PrintStream(output));
|
||||||
|
|
||||||
String actual = output.toString(UTF_8.name());
|
String actual = output.toString(UTF_8);
|
||||||
List<String> actualLines = Arrays.asList(actual.split(System.lineSeparator()));
|
List<String> actualLines = Arrays.asList(actual.split(System.lineSeparator()));
|
||||||
|
|
||||||
Class<?> testClass = testInfo.getTestClass().get();
|
Class<?> testClass = testInfo.getTestClass().get();
|
|
@ -1,113 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
-->
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>maven</artifactId>
|
|
||||||
<version>4.0.0-beta-5-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>maven-slf4j-provider</artifactId>
|
|
||||||
|
|
||||||
<name>Maven SLF4J Simple Provider</name>
|
|
||||||
<description>Maven SLF4J provider based on SLF4J's simple provider, extended to support Maven styled colors
|
|
||||||
for levels and stacktrace rendering.</description>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>maven-api-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>maven-jline</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.hamcrest</groupId>
|
|
||||||
<artifactId>hamcrest</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<artifactItems>
|
|
||||||
<artifactItem>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-simple</artifactId>
|
|
||||||
<version>${slf4jVersion}</version>
|
|
||||||
<type>jar</type>
|
|
||||||
<classifier>sources</classifier>
|
|
||||||
<overWrite>false</overWrite>
|
|
||||||
<outputDirectory>${project.build.directory}/generated-sources/slf4j-simple</outputDirectory>
|
|
||||||
<includes>org/slf4j/simple/*.java</includes>
|
|
||||||
</artifactItem>
|
|
||||||
</artifactItems>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>unzip-slf4j-simple</id>
|
|
||||||
<goals>
|
|
||||||
<goal>unpack</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>build-helper-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>add-slf4j-simple</id>
|
|
||||||
<goals>
|
|
||||||
<goal>add-source</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>generate-sources</phase>
|
|
||||||
<configuration>
|
|
||||||
<sources>
|
|
||||||
<source>${project.build.directory}/generated-sources/slf4j-simple</source>
|
|
||||||
</sources>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.maven.slf4j;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
|
||||||
import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.simple.SimpleLoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LogFactory for Maven which can create a simple logger or one which, if set, fails the build on a severity threshold.
|
|
||||||
*/
|
|
||||||
public class MavenLoggerFactory extends SimpleLoggerFactory implements MavenSlf4jWrapperFactory {
|
|
||||||
private LogLevelRecorder logLevelRecorder = null;
|
|
||||||
|
|
||||||
public MavenLoggerFactory() {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLogLevelRecorder(LogLevelRecorder logLevelRecorder) {
|
|
||||||
if (this.logLevelRecorder != null) {
|
|
||||||
throw new IllegalStateException("LogLevelRecorder has already been set.");
|
|
||||||
}
|
|
||||||
this.logLevelRecorder = logLevelRecorder;
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<LogLevelRecorder> getLogLevelRecorder() {
|
|
||||||
return Optional.ofNullable(logLevelRecorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Logger createLogger(String name) {
|
|
||||||
if (logLevelRecorder == null) {
|
|
||||||
return new MavenSimpleLogger(name);
|
|
||||||
} else {
|
|
||||||
return new MavenFailOnSeverityLogger(name, logLevelRecorder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package org.slf4j.simple;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class inheriting SimpleLogger to work around the fact that the {@link #write(StringBuilder, Throwable)}
|
|
||||||
* method is package private.
|
|
||||||
*/
|
|
||||||
public class ExtSimpleLogger extends SimpleLogger {
|
|
||||||
|
|
||||||
public ExtSimpleLogger(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void write(StringBuilder buf, Throwable t) {
|
|
||||||
doWrite(buf, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doWrite(StringBuilder buf, Throwable t) {
|
|
||||||
super.write(buf, t);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package org.apache.maven.logwrapper;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.slf4j.event.Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible for keeping state of whether the threshold of the --fail-on-severity flag has been hit.
|
|
||||||
*/
|
|
||||||
public class LogLevelRecorder {
|
|
||||||
private static final Map<String, Level> ACCEPTED_LEVELS = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
ACCEPTED_LEVELS.put("WARN", Level.WARN);
|
|
||||||
ACCEPTED_LEVELS.put("WARNING", Level.WARN);
|
|
||||||
ACCEPTED_LEVELS.put("ERROR", Level.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Level logThreshold;
|
|
||||||
private boolean metThreshold = false;
|
|
||||||
|
|
||||||
public LogLevelRecorder(String threshold) {
|
|
||||||
logThreshold = determineThresholdLevel(threshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Level determineThresholdLevel(String input) {
|
|
||||||
final Level result = ACCEPTED_LEVELS.get(input);
|
|
||||||
if (result == null) {
|
|
||||||
String message = String.format(
|
|
||||||
"%s is not a valid log severity threshold. Valid severities are WARN/WARNING and ERROR.", input);
|
|
||||||
throw new IllegalArgumentException(message);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void record(Level logLevel) {
|
|
||||||
if (!metThreshold && logLevel.toInt() >= logThreshold.toInt()) {
|
|
||||||
metThreshold = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean metThreshold() {
|
|
||||||
return metThreshold;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<project xmlns="http://maven.apache.org/DECORATION/1.8.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.8.0 http://maven.apache.org/xsd/decoration-1.8.0.xsd">
|
|
||||||
|
|
||||||
<edit>${project.scm.url}</edit>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<menu name="Overview">
|
|
||||||
<item name="Introduction" href="index.html"/>
|
|
||||||
<item name="Javadocs" href="apidocs/index.html"/>
|
|
||||||
<item name="Source Xref" href="xref/index.html"/>
|
|
||||||
<!--item name="FAQ" href="faq.html"/-->
|
|
||||||
</menu>
|
|
||||||
|
|
||||||
<menu ref="parent"/>
|
|
||||||
<menu ref="reports"/>
|
|
||||||
</body>
|
|
||||||
</project>
|
|
18
pom.xml
18
pom.xml
|
@ -109,14 +109,13 @@ under the License.
|
||||||
<module>maven-di</module>
|
<module>maven-di</module>
|
||||||
<module>maven-xml-impl</module>
|
<module>maven-xml-impl</module>
|
||||||
<module>maven-jline</module>
|
<module>maven-jline</module>
|
||||||
|
<module>maven-logging</module>
|
||||||
<module>maven-core</module>
|
<module>maven-core</module>
|
||||||
<module>maven-settings</module>
|
<module>maven-settings</module>
|
||||||
<module>maven-settings-builder</module>
|
<module>maven-settings-builder</module>
|
||||||
<module>maven-artifact</module>
|
<module>maven-artifact</module>
|
||||||
<module>maven-resolver-provider</module>
|
<module>maven-resolver-provider</module>
|
||||||
<module>maven-repository-metadata</module>
|
<module>maven-repository-metadata</module>
|
||||||
<module>maven-slf4j-provider</module>
|
|
||||||
<module>maven-slf4j-wrapper</module>
|
|
||||||
<module>maven-embedder</module>
|
<module>maven-embedder</module>
|
||||||
<module>maven-cli</module>
|
<module>maven-cli</module>
|
||||||
<module>maven-compat</module>
|
<module>maven-compat</module>
|
||||||
|
@ -215,6 +214,11 @@ under the License.
|
||||||
<artifactId>maven-jline</artifactId>
|
<artifactId>maven-jline</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-logging</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-core</artifactId>
|
<artifactId>maven-core</artifactId>
|
||||||
|
@ -340,11 +344,6 @@ under the License.
|
||||||
<artifactId>maven-toolchain-builder</artifactId>
|
<artifactId>maven-toolchain-builder</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-xml-impl</artifactId>
|
<artifactId>maven-xml-impl</artifactId>
|
||||||
|
@ -355,11 +354,6 @@ under the License.
|
||||||
<artifactId>maven-compat</artifactId>
|
<artifactId>maven-compat</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>maven-slf4j-provider</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!--bootstrap-end-comment-->
|
<!--bootstrap-end-comment-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
<groupId>org.codehaus.plexus</groupId>
|
||||||
|
|
Loading…
Reference in New Issue