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>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-slf4j-provider</artifactId>
|
||||
<artifactId>maven-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jline</groupId>
|
||||
|
|
|
@ -166,6 +166,14 @@ public interface Options {
|
|||
@Nonnull
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -175,7 +175,7 @@ under the License.
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
||||
<artifactId>maven-logging</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -181,6 +181,14 @@ public abstract class CommonsCliOptions implements Options {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Boolean> rawStreams() {
|
||||
if (commandLine.hasOption(CLIManager.RAW_STREAMS)) {
|
||||
return Optional.of(Boolean.TRUE);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> 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 LOG_FILE = "l";
|
||||
public static final String RAW_STREAMS = "raw-streams";
|
||||
public static final String COLOR = "color";
|
||||
public static final String HELP = "h";
|
||||
|
||||
|
@ -348,6 +357,10 @@ public abstract class CommonsCliOptions implements Options {
|
|||
.hasArg()
|
||||
.desc("Log file where all build output will go (disables output color)")
|
||||
.build());
|
||||
options.addOption(Option.builder()
|
||||
.longOpt(RAW_STREAMS)
|
||||
.desc("Do not decorate standard output and error streams")
|
||||
.build());
|
||||
options.addOption(Option.builder(SHOW_VERSION)
|
||||
.longOpt("show-version")
|
||||
.desc("Display version information WITHOUT stopping build")
|
||||
|
|
|
@ -122,6 +122,11 @@ public abstract class LayeredOptions<O extends Options> implements Options {
|
|||
return returnFirstPresentOrEmpty(Options::logFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Boolean> rawStreams() {
|
||||
return returnFirstPresentOrEmpty(Options::rawStreams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> color() {
|
||||
return returnFirstPresentOrEmpty(Options::color);
|
||||
|
|
|
@ -20,13 +20,14 @@ package org.apache.maven.cling.invoker;
|
|||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
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.Options;
|
||||
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.artifact.InvalidRepositoryException;
|
||||
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.Slf4jMavenTransferListener;
|
||||
import org.apache.maven.execution.MavenExecutionRequest;
|
||||
import org.apache.maven.jline.FastTerminal;
|
||||
import org.apache.maven.jline.MessageUtils;
|
||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
||||
import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
|
||||
import org.apache.maven.logging.BuildEventListener;
|
||||
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.Profile;
|
||||
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.SettingsBuildingResult;
|
||||
import org.apache.maven.settings.building.SettingsProblem;
|
||||
import org.apache.maven.slf4j.MavenSimpleLogger;
|
||||
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.LoggerFactory;
|
||||
import org.slf4j.spi.LocationAwareLogger;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
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> installationResolver;
|
||||
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) {
|
||||
this.invoker = invoker;
|
||||
|
@ -127,9 +135,6 @@ public abstract class LookupInvoker<
|
|||
.toAbsolutePath();
|
||||
this.userResolver = s ->
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -137,6 +142,8 @@ public abstract class LookupInvoker<
|
|||
public ILoggerFactory loggerFactory;
|
||||
public Slf4jConfiguration slf4jConfiguration;
|
||||
public Slf4jConfiguration.Level loggerLevel;
|
||||
public Terminal terminal;
|
||||
public BuildEventListener buildEventListener;
|
||||
public ClassLoader currentThreadContextClassLoader;
|
||||
public ContainerCapsule containerCapsule;
|
||||
public Lookup lookup;
|
||||
|
@ -149,10 +156,29 @@ public abstract class LookupInvoker<
|
|||
public Path userSettingsPath;
|
||||
public Settings effectiveSettings;
|
||||
|
||||
public final List<AutoCloseable> closeables = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void close() throws InvokerException {
|
||||
if (containerCapsule != null) {
|
||||
containerCapsule.close();
|
||||
List<Exception> causes = null;
|
||||
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
|
||||
// see https://issues.apache.org/jira/browse/MNG-2570
|
||||
|
||||
// LOG STREAMS
|
||||
if (mavenOptions.logFile().isPresent()) {
|
||||
Path logFile = context.cwdResolver.apply(mavenOptions.logFile().get());
|
||||
// redirect stdout and stderr to file
|
||||
try {
|
||||
PrintStream ps = new PrintStream(Files.newOutputStream(logFile));
|
||||
System.setOut(ps);
|
||||
System.setErr(ps);
|
||||
} catch (IOException e) {
|
||||
throw new InvokerException("Cannot set up log " + e.getMessage(), e);
|
||||
}
|
||||
// JLine is quite slow to start due to the native library unpacking and loading
|
||||
// so boot it asynchronously
|
||||
context.terminal = createTerminal(context);
|
||||
context.closeables.add(MessageUtils::systemUninstall);
|
||||
|
||||
// Create the build log appender
|
||||
ProjectBuildLogAppender projectBuildLogAppender =
|
||||
new ProjectBuildLogAppender(determineBuildEventListener(context));
|
||||
context.closeables.add(projectBuildLogAppender);
|
||||
}
|
||||
|
||||
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 {
|
||||
R invokerRequest = context.invokerRequest;
|
||||
Options mavenOptions = invokerRequest.options();
|
||||
|
@ -298,13 +381,19 @@ public abstract class LookupInvoker<
|
|||
|
||||
if (mavenOptions.failOnSeverity().isPresent()) {
|
||||
String logLevelThreshold = mavenOptions.failOnSeverity().get();
|
||||
|
||||
if (context.loggerFactory instanceof MavenSlf4jWrapperFactory) {
|
||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder(logLevelThreshold);
|
||||
((MavenSlf4jWrapperFactory) context.loggerFactory).setLogLevelRecorder(logLevelRecorder);
|
||||
if (context.loggerFactory instanceof LogLevelRecorder recorder) {
|
||||
LogLevelRecorder.Level level =
|
||||
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);
|
||||
context.logger.info("Enabled to break the build on log level " + logLevelThreshold + ".");
|
||||
} 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 '"
|
||||
+ context.loggerFactory.getClass().getName() + "' instead. "
|
||||
+ "The --fail-on-severity flag will not take effect.");
|
||||
|
@ -315,14 +404,14 @@ public abstract class LookupInvoker<
|
|||
protected void helpOrVersionAndMayExit(C context) throws Exception {
|
||||
R invokerRequest = context.invokerRequest;
|
||||
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);
|
||||
}
|
||||
if (invokerRequest.options().showVersionAndExit().isPresent()) {
|
||||
if (invokerRequest.options().quiet().orElse(false)) {
|
||||
context.stdOut.println(CLIReportingUtils.showVersionMinimal());
|
||||
context.terminal.writer().println(CLIReportingUtils.showVersionMinimal());
|
||||
} else {
|
||||
context.stdOut.println(CLIReportingUtils.showVersion());
|
||||
context.terminal.writer().println(CLIReportingUtils.showVersion());
|
||||
}
|
||||
throw new ExitException(0);
|
||||
}
|
||||
|
@ -331,12 +420,13 @@ public abstract class LookupInvoker<
|
|||
protected void preCommands(C context) throws Exception {
|
||||
Options mavenOptions = context.invokerRequest.options();
|
||||
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 {
|
||||
context.containerCapsule = createContainerCapsuleFactory().createContainerCapsule(context);
|
||||
context.closeables.add(context.containerCapsule);
|
||||
context.lookup = context.containerCapsule.getLookup();
|
||||
context.settingsBuilder = context.lookup.lookup(SettingsBuilder.class);
|
||||
|
||||
|
@ -704,7 +794,7 @@ public abstract class LookupInvoker<
|
|||
} else if (context.interactive && !logFile) {
|
||||
return new SimplexTransferListener(new ConsoleMavenTransferListener(
|
||||
context.invokerRequest.messageBuilderFactory(),
|
||||
context.stdOut,
|
||||
context.terminal.writer(),
|
||||
context.invokerRequest.options().verbose().orElse(false)));
|
||||
} else {
|
||||
return new Slf4jMavenTransferListener();
|
||||
|
|
|
@ -56,6 +56,8 @@ import org.apache.maven.execution.ProfileActivation;
|
|||
import org.apache.maven.execution.ProjectActivation;
|
||||
import org.apache.maven.jline.MessageUtils;
|
||||
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.project.MavenProject;
|
||||
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.codehaus.plexus.PlexusContainer;
|
||||
import org.eclipse.aether.DefaultRepositoryCache;
|
||||
import org.eclipse.aether.transfer.TransferListener;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static org.apache.maven.cling.invoker.Utils.toProperties;
|
||||
|
@ -359,12 +362,17 @@ public abstract class DefaultMavenInvoker<
|
|||
}
|
||||
|
||||
protected ExecutionListener determineExecutionListener(C context) {
|
||||
ExecutionListener executionListener = new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
|
||||
ExecutionListener listener = new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
|
||||
if (context.eventSpyDispatcher != null) {
|
||||
return context.eventSpyDispatcher.chainListener(executionListener);
|
||||
} else {
|
||||
return executionListener;
|
||||
listener = context.eventSpyDispatcher.chainListener(listener);
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -39,8 +39,11 @@ public abstract class MavenInvokerTestSupport<O extends MavenOptions, R extends
|
|||
|
||||
protected void invoke(Path cwd, Collection<String> goals) throws Exception {
|
||||
// works only in recent Maven4
|
||||
Assumptions.assumeTrue(Files.isRegularFile(
|
||||
Paths.get(System.getProperty("maven.home")).resolve("conf").resolve("maven.properties")));
|
||||
Assumptions.assumeTrue(
|
||||
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"));
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ under the License.
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-slf4j-provider</artifactId>
|
||||
<artifactId>maven-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
|
|
|
@ -93,9 +93,6 @@ public class MultiThreadedBuilder implements Builder {
|
|||
ExecutorService executor = Executors.newFixedThreadPool(nThreads, new BuildThreadFactory());
|
||||
CompletionService<ProjectSegment> service = new ExecutorCompletionService<>(executor);
|
||||
|
||||
// Currently disabled
|
||||
ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( analyzer.getProjectBuilds(), System.out );
|
||||
|
||||
for (TaskSegment taskSegment : taskSegments) {
|
||||
ProjectBuildList segmentProjectBuilds = projectBuilds.getByTaskSegment(taskSegment);
|
||||
Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment(taskSegment);
|
||||
|
@ -103,7 +100,7 @@ public class MultiThreadedBuilder implements Builder {
|
|||
ConcurrencyDependencyGraph analyzer =
|
||||
new ConcurrencyDependencyGraph(segmentProjectBuilds, session.getProjectDependencyGraph());
|
||||
multiThreadedProjectTaskSegmentBuild(
|
||||
analyzer, reactorContext, session, service, taskSegment, projectBuildMap, muxer);
|
||||
analyzer, reactorContext, session, service, taskSegment, projectBuildMap);
|
||||
if (reactorContext.getReactorBuildStatus().isHalted()) {
|
||||
break;
|
||||
}
|
||||
|
@ -123,8 +120,7 @@ public class MultiThreadedBuilder implements Builder {
|
|||
MavenSession rootSession,
|
||||
CompletionService<ProjectSegment> service,
|
||||
TaskSegment taskSegment,
|
||||
Map<MavenProject, ProjectSegment> projectBuildList,
|
||||
ThreadOutputMuxer muxer) {
|
||||
Map<MavenProject, ProjectSegment> projectBuildList) {
|
||||
// gather artifactIds which are not unique so that the respective thread names can be extended with the groupId
|
||||
Set<String> duplicateArtifactIds = projectBuildList.keySet().stream()
|
||||
.map(MavenProject::getArtifactId)
|
||||
|
@ -139,8 +135,8 @@ public class MultiThreadedBuilder implements Builder {
|
|||
for (MavenProject mavenProject : analyzer.getRootSchedulableBuilds()) {
|
||||
ProjectSegment projectSegment = projectBuildList.get(mavenProject);
|
||||
logger.debug("Scheduling: {}", projectSegment.getProject());
|
||||
Callable<ProjectSegment> cb = createBuildCallable(
|
||||
rootSession, projectSegment, reactorContext, taskSegment, muxer, duplicateArtifactIds);
|
||||
Callable<ProjectSegment> cb =
|
||||
createBuildCallable(rootSession, projectSegment, reactorContext, taskSegment, duplicateArtifactIds);
|
||||
service.submit(cb);
|
||||
}
|
||||
|
||||
|
@ -160,12 +156,7 @@ public class MultiThreadedBuilder implements Builder {
|
|||
ProjectSegment scheduledDependent = projectBuildList.get(mavenProject);
|
||||
logger.debug("Scheduling: {}", scheduledDependent);
|
||||
Callable<ProjectSegment> cb = createBuildCallable(
|
||||
rootSession,
|
||||
scheduledDependent,
|
||||
reactorContext,
|
||||
taskSegment,
|
||||
muxer,
|
||||
duplicateArtifactIds);
|
||||
rootSession, scheduledDependent, reactorContext, taskSegment, duplicateArtifactIds);
|
||||
service.submit(cb);
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +176,6 @@ public class MultiThreadedBuilder implements Builder {
|
|||
final ProjectSegment projectBuild,
|
||||
final ReactorContext reactorContext,
|
||||
final TaskSegment taskSegment,
|
||||
final ThreadOutputMuxer muxer,
|
||||
final Set<String> duplicateArtifactIds) {
|
||||
return () -> {
|
||||
final Thread currentThread = Thread.currentThread();
|
||||
|
@ -198,10 +188,8 @@ public class MultiThreadedBuilder implements Builder {
|
|||
currentThread.setName("mvn-builder-" + threadNameSuffix);
|
||||
|
||||
try {
|
||||
// muxer.associateThreadWithProjectSegment( projectBuild );
|
||||
lifecycleModuleBuilder.buildProject(
|
||||
projectBuild.getSession(), rootSession, reactorContext, project, taskSegment);
|
||||
// muxer.setThisModuleComplete( projectBuild );
|
||||
|
||||
return projectBuild;
|
||||
} 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 ReactorContext reactorContext;
|
||||
final PhasingExecutor executor;
|
||||
final ConcurrentLogOutput appender;
|
||||
final Map<Object, Clock> clocks = new ConcurrentHashMap<>();
|
||||
final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
final int threads;
|
||||
|
@ -179,7 +178,6 @@ public class BuildPlanExecutor {
|
|||
// Propagate the parallel flag to the root session
|
||||
session.setParallel(threads > 1);
|
||||
this.executor = new PhasingExecutor(Executors.newFixedThreadPool(threads, new BuildThreadFactory()));
|
||||
this.appender = new ConcurrentLogOutput();
|
||||
|
||||
// build initial plan
|
||||
this.plan = buildInitialPlan(taskSegments);
|
||||
|
@ -190,7 +188,6 @@ public class BuildPlanExecutor {
|
|||
this.reactorContext = null;
|
||||
this.threads = 1;
|
||||
this.executor = null;
|
||||
this.appender = null;
|
||||
this.plan = null;
|
||||
}
|
||||
|
||||
|
@ -312,7 +309,6 @@ public class BuildPlanExecutor {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
this.appender.close();
|
||||
this.executor.close();
|
||||
}
|
||||
|
||||
|
@ -331,7 +327,7 @@ public class BuildPlanExecutor {
|
|||
.forEach(step -> {
|
||||
boolean nextIsPlanning = step.successors.stream().anyMatch(st -> PLAN.equals(st.name));
|
||||
executor.execute(() -> {
|
||||
try (AutoCloseable ctx = appender.build(step.project)) {
|
||||
try {
|
||||
executeStep(step);
|
||||
if (nextIsPlanning) {
|
||||
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
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.maven.logwrapper;
|
||||
package org.apache.maven.logging;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.ILoggerFactory;
|
||||
import org.apache.maven.execution.ExecutionEvent;
|
||||
import org.eclipse.aether.transfer.TransferEvent;
|
||||
|
||||
/**
|
||||
* Wrapper for creating loggers which can have a log level threshold.
|
||||
* An abstract build event sink.
|
||||
*/
|
||||
public interface MavenSlf4jWrapperFactory extends ILoggerFactory {
|
||||
void setLogLevelRecorder(LogLevelRecorder logLevelRecorder);
|
||||
public interface BuildEventListener {
|
||||
|
||||
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"
|
||||
+ exceptions.stream()
|
||||
.map(Throwable::getMessage)
|
||||
.collect(Collectors.joining(System.lineSeparator() + "\t"))
|
||||
+ System.lineSeparator(),
|
||||
.collect(Collectors.joining(System.lineSeparator() + "\t")),
|
||||
cause);
|
||||
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>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
||||
<artifactId>maven-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
|
|
|
@ -38,6 +38,7 @@ import java.util.HashSet;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
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.MessageUtils;
|
||||
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
||||
import org.apache.maven.logwrapper.MavenSlf4jWrapperFactory;
|
||||
import org.apache.maven.logging.api.LogLevelRecorder;
|
||||
import org.apache.maven.model.building.ModelProcessor;
|
||||
import org.apache.maven.model.root.RootLocator;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
|
@ -541,15 +541,22 @@ public class MavenCli {
|
|||
if (commandLine.hasOption(CLIManager.FAIL_ON_SEVERITY)) {
|
||||
String logLevelThreshold = commandLine.getOptionValue(CLIManager.FAIL_ON_SEVERITY);
|
||||
|
||||
if (slf4jLoggerFactory instanceof MavenSlf4jWrapperFactory) {
|
||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder(logLevelThreshold);
|
||||
((MavenSlf4jWrapperFactory) slf4jLoggerFactory).setLogLevelRecorder(logLevelRecorder);
|
||||
if (slf4jLoggerFactory instanceof LogLevelRecorder recorder) {
|
||||
LogLevelRecorder.Level level =
|
||||
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);
|
||||
} else {
|
||||
slf4jLogger.warn(
|
||||
"Expected LoggerFactory to be of type '{}', but found '{}' instead. "
|
||||
+ "The --fail-on-severity flag will not take effect.",
|
||||
MavenSlf4jWrapperFactory.class.getName(),
|
||||
LogLevelRecorder.class.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.MavenExecutionResult;
|
||||
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.descriptor.MojoDescriptor;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
|
@ -79,13 +77,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
|||
}
|
||||
|
||||
private static String chars(char c, int count) {
|
||||
StringBuilder buffer = new StringBuilder(count);
|
||||
|
||||
for (int i = count; i > 0; i--) {
|
||||
buffer.append(c);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
return String.valueOf(c).repeat(Math.max(0, count));
|
||||
}
|
||||
|
||||
private void infoLine(char c) {
|
||||
|
@ -137,9 +129,8 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
|||
}
|
||||
|
||||
final List<MavenProject> allProjects = event.getSession().getAllProjects();
|
||||
final int projectsSkipped = allProjects.size() - projects.size();
|
||||
|
||||
currentVisitedProjectCount = projectsSkipped;
|
||||
currentVisitedProjectCount = allProjects.size() - projects.size();
|
||||
totalProjects = allProjects.size();
|
||||
}
|
||||
}
|
||||
|
@ -154,16 +145,13 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
|||
|
||||
ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory();
|
||||
|
||||
if (iLoggerFactory instanceof MavenSlf4jWrapperFactory) {
|
||||
MavenSlf4jWrapperFactory loggerFactory = (MavenSlf4jWrapperFactory) iLoggerFactory;
|
||||
loggerFactory
|
||||
.getLogLevelRecorder()
|
||||
.filter(LogLevelRecorder::metThreshold)
|
||||
.ifPresent(recorder -> event.getSession()
|
||||
.getResult()
|
||||
.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).")));
|
||||
if (iLoggerFactory instanceof org.apache.maven.logging.api.LogLevelRecorder recorder
|
||||
&& recorder.hasReachedMaxLevel()) {
|
||||
event.getSession()
|
||||
.getResult()
|
||||
.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());
|
||||
|
@ -297,7 +285,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
|
|||
infoLine('-');
|
||||
String name = event.getProject().getName();
|
||||
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('-');
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.apache.maven.cli.logging;
|
||||
|
||||
import org.apache.maven.logging.ProjectBuildLogAppender;
|
||||
import org.codehaus.plexus.logging.Logger;
|
||||
|
||||
/**
|
||||
|
@ -29,16 +30,20 @@ import org.codehaus.plexus.logging.Logger;
|
|||
public class Slf4jLogger implements Logger {
|
||||
|
||||
private org.slf4j.Logger logger;
|
||||
private String projectId;
|
||||
|
||||
public Slf4jLogger(org.slf4j.Logger logger) {
|
||||
this.logger = logger;
|
||||
this.projectId = ProjectBuildLogAppender.getProjectId();
|
||||
}
|
||||
|
||||
public void debug(String message) {
|
||||
setMdc();
|
||||
logger.debug(message);
|
||||
}
|
||||
|
||||
public void debug(String message, Throwable throwable) {
|
||||
setMdc();
|
||||
logger.debug(message, throwable);
|
||||
}
|
||||
|
||||
|
@ -47,10 +52,12 @@ public class Slf4jLogger implements Logger {
|
|||
}
|
||||
|
||||
public void info(String message) {
|
||||
setMdc();
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
public void info(String message, Throwable throwable) {
|
||||
setMdc();
|
||||
logger.info(message, throwable);
|
||||
}
|
||||
|
||||
|
@ -59,10 +66,12 @@ public class Slf4jLogger implements Logger {
|
|||
}
|
||||
|
||||
public void warn(String message) {
|
||||
setMdc();
|
||||
logger.warn(message);
|
||||
}
|
||||
|
||||
public void warn(String message, Throwable throwable) {
|
||||
setMdc();
|
||||
logger.warn(message, throwable);
|
||||
}
|
||||
|
||||
|
@ -71,10 +80,12 @@ public class Slf4jLogger implements Logger {
|
|||
}
|
||||
|
||||
public void error(String message) {
|
||||
setMdc();
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
public void error(String message, Throwable throwable) {
|
||||
setMdc();
|
||||
logger.error(message, throwable);
|
||||
}
|
||||
|
||||
|
@ -83,10 +94,12 @@ public class Slf4jLogger implements Logger {
|
|||
}
|
||||
|
||||
public void fatalError(String message) {
|
||||
setMdc();
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
public void fatalError(String message, Throwable throwable) {
|
||||
setMdc();
|
||||
logger.error(message, throwable);
|
||||
}
|
||||
|
||||
|
@ -116,4 +129,10 @@ public class Slf4jLogger implements Logger {
|
|||
public String 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;
|
||||
|
||||
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.
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class Slf4jSimpleConfiguration extends BaseSlf4jConfiguration {
|
||||
public class MavenSimpleConfiguration extends BaseSlf4jConfiguration {
|
||||
@Override
|
||||
public void setRootLoggerLevel(Level level) {
|
||||
String value;
|
||||
|
@ -48,7 +50,9 @@ public class Slf4jSimpleConfiguration extends BaseSlf4jConfiguration {
|
|||
|
||||
@Override
|
||||
public void activate() {
|
||||
// property for root logger level or System.out redirection need to be taken into account
|
||||
MavenSlf4jSimpleFriend.init();
|
||||
ILoggerFactory lf = LoggerFactory.getILoggerFactory();
|
||||
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
|
||||
# value = corresponding o.a.m.cli.logging.Slf4jConfiguration class
|
||||
org.slf4j.impl.SimpleLoggerFactory=org.apache.maven.cli.logging.impl.Slf4jSimpleConfiguration
|
||||
org.apache.maven.slf4j.MavenLoggerFactory=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.MavenSimpleConfiguration
|
||||
org.apache.logging.slf4j.Log4jLoggerFactory=org.apache.maven.cli.logging.impl.Log4j2Configuration
|
||||
ch.qos.logback.classic.LoggerContext=org.apache.maven.cli.logging.impl.LogbackConfiguration
|
||||
|
|
|
@ -98,4 +98,8 @@ public class MessageUtils {
|
|||
public static MessageBuilder 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">
|
||||
<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-wrapper</artifactId>
|
||||
|
||||
<name>Maven SLF4J Wrapper</name>
|
||||
<description>This modules provides an ILoggerFactory interface which avoids a cyclic dependency between maven-embedder and maven-slf4j-provider.</description>
|
||||
<artifactId>maven-logging</artifactId>
|
||||
<name>Maven Logging</name>
|
||||
<description>Provides the Maven Logging infrastructure</description>
|
||||
|
||||
<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>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
|
@ -48,4 +54,14 @@ under the License.
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.eclipse.sisu</groupId>
|
||||
<artifactId>sisu-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -16,17 +16,22 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.slf4j;
|
||||
package org.apache.maven.logging.api;
|
||||
|
||||
/**
|
||||
* Utility for Maven to access Slf4j internals through package access.
|
||||
* Use with precaution, since this is not normally intended for production use.
|
||||
*/
|
||||
public class MavenSlf4jFriend {
|
||||
/**
|
||||
* Reset Slf4j internal state.
|
||||
*/
|
||||
public static void reset() {
|
||||
LoggerFactory.reset();
|
||||
public interface LogLevelRecorder {
|
||||
|
||||
enum Level {
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
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;
|
||||
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import org.slf4j.ILoggerFactory;
|
||||
import org.slf4j.IMarkerFactory;
|
||||
import org.slf4j.helpers.BasicMDCAdapter;
|
||||
import org.slf4j.helpers.BasicMarkerFactory;
|
||||
import org.slf4j.helpers.NOPMDCAdapter;
|
||||
import org.slf4j.spi.MDCAdapter;
|
||||
import org.slf4j.spi.SLF4JServiceProvider;
|
||||
|
||||
|
@ -35,9 +37,13 @@ public class MavenServiceProvider implements SLF4JServiceProvider {
|
|||
@SuppressWarnings({"checkstyle:StaticVariableName", "checkstyle:VisibilityModifier"})
|
||||
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 MDCAdapter mdcAdapter = new NOPMDCAdapter();
|
||||
private MDCAdapter mdcAdapter = new BasicMDCAdapter();
|
||||
|
||||
protected MavenLoggerFactory loadMavenLoggerFactory() {
|
||||
return ServiceLoader.load(MavenLoggerFactory.class).findFirst().orElseGet(MavenLoggerFactory::new);
|
||||
}
|
||||
|
||||
public ILoggerFactory getLoggerFactory() {
|
||||
return loggerFactory;
|
|
@ -22,7 +22,6 @@ import java.io.PrintStream;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.maven.api.services.MessageBuilder;
|
||||
import org.slf4j.simple.ExtSimpleLogger;
|
||||
|
||||
import static org.apache.maven.jline.MessageUtils.builder;
|
||||
|
||||
|
@ -32,7 +31,7 @@ import static org.apache.maven.jline.MessageUtils.builder;
|
|||
*
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public class MavenSimpleLogger extends ExtSimpleLogger {
|
||||
public class MavenSimpleLogger extends MavenBaseLogger {
|
||||
|
||||
private String traceRenderedLevel;
|
||||
private String debugRenderedLevel;
|
||||
|
@ -74,18 +73,24 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(StringBuilder buf, Throwable t) {
|
||||
protected void write(StringBuilder buf, Throwable t) {
|
||||
Consumer<String> sink = logSink;
|
||||
if (sink != null) {
|
||||
sink.accept(buf.toString());
|
||||
if (t != null) {
|
||||
writeThrowable(t, sink);
|
||||
}
|
||||
} else {
|
||||
super.doWrite(buf, t);
|
||||
super.write(buf, t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeThrowable(Throwable t, PrintStream stream) {
|
||||
writeThrowable(t, stream::println);
|
||||
}
|
||||
|
||||
protected void writeThrowable(Throwable t, Consumer<String> stream) {
|
||||
if (t == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -93,12 +98,12 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
|||
if (t.getMessage() != null) {
|
||||
builder.a(": ").failure(t.getMessage());
|
||||
}
|
||||
stream.println(builder);
|
||||
stream.accept(builder.toString());
|
||||
|
||||
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();
|
||||
for (StackTraceElement e : t.getStackTrace()) {
|
||||
builder.a(prefix);
|
||||
|
@ -111,7 +116,7 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
|||
builder.a("(");
|
||||
builder.strong(getLocation(e));
|
||||
builder.a(")");
|
||||
stream.println(builder);
|
||||
stream.accept(builder.toString());
|
||||
builder.setLength(0);
|
||||
}
|
||||
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 =
|
||||
builder().a(prefix).strong(caption).a(": ").a(t.getClass().getName());
|
||||
if (t.getMessage() != null) {
|
||||
builder.a(": ").failure(t.getMessage());
|
||||
}
|
||||
stream.println(builder);
|
||||
stream.accept(builder.toString());
|
||||
|
||||
printStackTrace(t, stream, prefix);
|
||||
}
|
||||
|
@ -147,4 +152,17 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
|
|||
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
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.maven.logwrapper;
|
||||
package org.apache.maven.slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.event.Level;
|
||||
|
@ -29,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
class LogLevelRecorderTest {
|
||||
@Test
|
||||
void createsLogLevelRecorder() {
|
||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder("WARN");
|
||||
DefaultLogLevelRecorder logLevelRecorder = new DefaultLogLevelRecorder("WARN");
|
||||
logLevelRecorder.record(Level.ERROR);
|
||||
|
||||
assertTrue(logLevelRecorder.metThreshold());
|
||||
|
@ -37,12 +37,12 @@ class LogLevelRecorderTest {
|
|||
|
||||
@Test
|
||||
void failsOnLowerThanWarn() {
|
||||
assertThrows(IllegalArgumentException.class, () -> new LogLevelRecorder("INFO"));
|
||||
assertThrows(IllegalArgumentException.class, () -> new DefaultLogLevelRecorder("INFO"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsLogLevelRecorderWithWarning() {
|
||||
LogLevelRecorder logLevelRecorder = new LogLevelRecorder("WARNING");
|
||||
DefaultLogLevelRecorder logLevelRecorder = new DefaultLogLevelRecorder("WARNING");
|
||||
logLevelRecorder.record(Level.ERROR);
|
||||
|
||||
assertTrue(logLevelRecorder.metThreshold());
|
||||
|
@ -50,7 +50,7 @@ class LogLevelRecorderTest {
|
|||
|
||||
@Test
|
||||
void failsOnUnknownLogLevel() {
|
||||
Throwable thrown = assertThrows(IllegalArgumentException.class, () -> new LogLevelRecorder("SEVERE"));
|
||||
Throwable thrown = assertThrows(IllegalArgumentException.class, () -> new DefaultLogLevelRecorder("SEVERE"));
|
||||
String message = thrown.getMessage();
|
||||
assertThat(message, containsString("SEVERE is not a valid log severity threshold"));
|
||||
assertThat(message, containsString("WARN"));
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package org.apache.maven.slf4j;
|
||||
|
||||
import org.apache.maven.logwrapper.LogLevelRecorder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
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.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class MavenLoggerFactoryTest {
|
||||
|
@ -58,31 +56,18 @@ class MavenLoggerFactoryTest {
|
|||
@Test
|
||||
void reportsWhenFailOnSeverityThresholdHasBeenHit() {
|
||||
MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
|
||||
mavenLoggerFactory.setLogLevelRecorder(new LogLevelRecorder("ERROR"));
|
||||
|
||||
assertTrue(mavenLoggerFactory.getLogLevelRecorder().isPresent());
|
||||
LogLevelRecorder logLevelRecorder =
|
||||
mavenLoggerFactory.getLogLevelRecorder().get();
|
||||
mavenLoggerFactory.logLevelRecorder = new DefaultLogLevelRecorder("ERROR");
|
||||
|
||||
MavenFailOnSeverityLogger logger = (MavenFailOnSeverityLogger) mavenLoggerFactory.getLogger("Test");
|
||||
assertFalse(logLevelRecorder.metThreshold());
|
||||
assertFalse(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||
|
||||
logger.warn("This should not hit the fail threshold");
|
||||
assertFalse(logLevelRecorder.metThreshold());
|
||||
assertFalse(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||
|
||||
logger.error("This should hit the fail threshold");
|
||||
assertTrue(logLevelRecorder.metThreshold());
|
||||
assertTrue(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||
|
||||
logger.warn("This should not reset the fail threshold");
|
||||
assertTrue(logLevelRecorder.metThreshold());
|
||||
}
|
||||
|
||||
@Test
|
||||
void failOnSeverityThresholdCanOnlyBeSetOnce() {
|
||||
MavenLoggerFactory mavenLoggerFactory = new MavenLoggerFactory();
|
||||
mavenLoggerFactory.setLogLevelRecorder(new LogLevelRecorder("WARN"));
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> mavenLoggerFactory.setLogLevelRecorder(new LogLevelRecorder("ERROR")));
|
||||
assertTrue(mavenLoggerFactory.logLevelRecorder.metThreshold());
|
||||
}
|
||||
}
|
|
@ -49,7 +49,7 @@ class MavenSimpleLoggerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void includesCauseAndSuppressedExceptionsWhenWritingThrowables(TestInfo testInfo) throws Exception {
|
||||
void includesCauseAndSuppressedExceptionsWhenWritingThrowables(TestInfo testInfo) {
|
||||
Exception causeOfSuppressed = new NoSuchElementException("cause of suppressed");
|
||||
Exception suppressed = new IllegalStateException("suppressed", causeOfSuppressed);
|
||||
suppressed.addSuppressed(new IllegalArgumentException(
|
||||
|
@ -62,7 +62,7 @@ class MavenSimpleLoggerTest {
|
|||
|
||||
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()));
|
||||
|
||||
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-xml-impl</module>
|
||||
<module>maven-jline</module>
|
||||
<module>maven-logging</module>
|
||||
<module>maven-core</module>
|
||||
<module>maven-settings</module>
|
||||
<module>maven-settings-builder</module>
|
||||
<module>maven-artifact</module>
|
||||
<module>maven-resolver-provider</module>
|
||||
<module>maven-repository-metadata</module>
|
||||
<module>maven-slf4j-provider</module>
|
||||
<module>maven-slf4j-wrapper</module>
|
||||
<module>maven-embedder</module>
|
||||
<module>maven-cli</module>
|
||||
<module>maven-compat</module>
|
||||
|
@ -215,6 +214,11 @@ under the License.
|
|||
<artifactId>maven-jline</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-logging</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-core</artifactId>
|
||||
|
@ -340,11 +344,6 @@ under the License.
|
|||
<artifactId>maven-toolchain-builder</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-slf4j-wrapper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-xml-impl</artifactId>
|
||||
|
@ -355,11 +354,6 @@ under the License.
|
|||
<artifactId>maven-compat</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-slf4j-provider</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!--bootstrap-end-comment-->
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
|
|
Loading…
Reference in New Issue