diff --git a/apache-maven/pom.xml b/apache-maven/pom.xml
index 45b017fbc56c..857e2d88cc14 100644
--- a/apache-maven/pom.xml
+++ b/apache-maven/pom.xml
@@ -99,7 +99,7 @@ under the License.
org.apache.maven
- maven-slf4j-provider
+ maven-loggingorg.jline
diff --git a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java
index 5a486bf2152f..128e23f630a8 100644
--- a/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java
+++ b/api/maven-api-cli/src/main/java/org/apache/maven/api/cli/Options.java
@@ -166,6 +166,14 @@ public interface Options {
@Nonnull
Optional logFile();
+ /**
+ * Returns whether raw streams should be logged.
+ *
+ * @return a boolean indicating whether raw streams should be logged
+ */
+ @Nonnull
+ Optional rawStreams();
+
/**
* Returns the color setting for console output.
*
diff --git a/maven-bom/pom.xml b/maven-bom/pom.xml
index 9e4f5c757391..164027d419c9 100644
--- a/maven-bom/pom.xml
+++ b/maven-bom/pom.xml
@@ -175,7 +175,7 @@ under the License.
org.apache.maven
- maven-slf4j-wrapper
+ maven-logging${project.version}
diff --git a/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java b/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
index 5ea9ec72dbae..ba65331388fe 100644
--- a/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
+++ b/maven-cli/src/main/java/org/apache/maven/cling/ClingSupport.java
@@ -25,7 +25,6 @@
import org.apache.maven.api.cli.InvokerRequest;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.ParserException;
-import org.apache.maven.jline.MessageUtils;
import org.codehaus.plexus.classworlds.ClassWorld;
import static java.util.Objects.requireNonNull;
@@ -65,8 +64,6 @@ private ClingSupport(ClassWorld classWorld, boolean classWorldManaged) {
* The main entry point.
*/
public int run(String[] args) throws IOException {
- MessageUtils.systemInstall();
- MessageUtils.registerShutdownHook();
try (Invoker invoker = createInvoker()) {
return invoker.invoke(parseArguments(args));
} catch (ParserException e) {
@@ -75,12 +72,8 @@ public int run(String[] args) throws IOException {
} catch (InvokerException e) {
return 1;
} finally {
- try {
- if (classWorldManaged) {
- classWorld.close();
- }
- } finally {
- MessageUtils.systemUninstall();
+ if (classWorldManaged) {
+ classWorld.close();
}
}
}
diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java
index c3adcb5ee10f..3e99afa8f044 100644
--- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java
+++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/CommonsCliOptions.java
@@ -181,6 +181,14 @@ public Optional logFile() {
return Optional.empty();
}
+ @Override
+ public Optional rawStreams() {
+ if (commandLine.hasOption(CLIManager.RAW_STREAMS)) {
+ return Optional.of(Boolean.TRUE);
+ }
+ return Optional.empty();
+ }
+
@Override
public Optional color() {
if (commandLine.hasOption(CLIManager.COLOR)) {
@@ -262,6 +270,7 @@ protected static class CLIManager {
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 @@ protected void prepareOptions(org.apache.commons.cli.Options 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")
diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java
index e702c76dde69..83219eb27b3d 100644
--- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java
+++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LayeredOptions.java
@@ -122,6 +122,11 @@ public Optional logFile() {
return returnFirstPresentOrEmpty(Options::logFile);
}
+ @Override
+ public Optional rawStreams() {
+ return returnFirstPresentOrEmpty(Options::rawStreams);
+ }
+
@Override
public Optional color() {
return returnFirstPresentOrEmpty(Options::color);
diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java
index b4995b6c0e13..d38077238dd0 100644
--- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java
+++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java
@@ -20,13 +20,14 @@
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.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.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.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 static class LookupInvokerContext<
public final Function cwdResolver;
public final Function installationResolver;
public final Function userResolver;
- public final InputStream stdIn;
- public final PrintWriter stdOut;
- public final PrintWriter stdErr;
protected LookupInvokerContext(LookupInvoker invoker, R invokerRequest) {
this.invoker = invoker;
@@ -127,9 +135,6 @@ protected LookupInvokerContext(LookupInvoker invoker, R invokerRequest)
.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 @@ protected LookupInvokerContext(LookupInvoker invoker, R invokerRequest)
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 @@ protected LookupInvokerContext(LookupInvoker invoker, R invokerRequest)
public Path userSettingsPath;
public Settings effectiveSettings;
+ public final List closeables = new ArrayList<>();
+
@Override
public void close() throws InvokerException {
- if (containerCapsule != null) {
- containerCapsule.close();
+ List causes = null;
+ List 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,18 +298,75 @@ protected void configureLogging(C context) throws Exception {
// 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
+ // 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 {
- PrintStream ps = new PrintStream(Files.newOutputStream(logFile));
- System.setOut(ps);
- System.setErr(ps);
+ PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(logFile));
+ bel = new SimpleBuildEventListener(printWriter::println);
} catch (IOException e) {
- throw new InvokerException("Cannot set up log " + e.getMessage(), 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 {
@@ -298,13 +381,19 @@ protected void activateLogging(C context) throws Exception {
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 @@ protected void activateLogging(C context) throws Exception {
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 @@ protected void helpOrVersionAndMayExit(C context) throws Exception {
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 @@ protected TransferListener determineTransferListener(C context, boolean noTransf
} 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();
diff --git a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java
index 3c8f6bb8f9d1..e8d4ed23d10c 100644
--- a/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java
+++ b/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/DefaultMavenInvoker.java
@@ -56,6 +56,8 @@
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.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 @@ protected String determineGlobalChecksumPolicy(C context) {
}
protected ExecutionListener determineExecutionListener(C context) {
- ExecutionListener executionListener = new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
+ ExecutionListener listener = new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
if (context.eventSpyDispatcher != null) {
- 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) {
diff --git a/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
index da6b4deb0be4..e2dd847053a8 100644
--- a/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
+++ b/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvn/MavenInvokerTestSupport.java
@@ -39,8 +39,11 @@ public abstract class MavenInvokerTestSupport 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"));
diff --git a/maven-core/pom.xml b/maven-core/pom.xml
index 6c44bac11cf6..c3baf1706c64 100644
--- a/maven-core/pom.xml
+++ b/maven-core/pom.xml
@@ -93,7 +93,7 @@ under the License.
org.apache.maven
- maven-slf4j-provider
+ maven-loggingorg.apache.maven.resolver
diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java
index 2a76afb50d92..6f13050ebd62 100644
--- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java
+++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/MultiThreadedBuilder.java
@@ -93,9 +93,6 @@ public void build(
ExecutorService executor = Executors.newFixedThreadPool(nThreads, new BuildThreadFactory());
CompletionService 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 projectBuildMap = projectBuilds.selectSegment(taskSegment);
@@ -103,7 +100,7 @@ public void build(
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 @@ private void multiThreadedProjectTaskSegmentBuild(
MavenSession rootSession,
CompletionService service,
TaskSegment taskSegment,
- Map projectBuildList,
- ThreadOutputMuxer muxer) {
+ Map projectBuildList) {
// gather artifactIds which are not unique so that the respective thread names can be extended with the groupId
Set duplicateArtifactIds = projectBuildList.keySet().stream()
.map(MavenProject::getArtifactId)
@@ -139,8 +135,8 @@ private void multiThreadedProjectTaskSegmentBuild(
for (MavenProject mavenProject : analyzer.getRootSchedulableBuilds()) {
ProjectSegment projectSegment = projectBuildList.get(mavenProject);
logger.debug("Scheduling: {}", projectSegment.getProject());
- Callable cb = createBuildCallable(
- rootSession, projectSegment, reactorContext, taskSegment, muxer, duplicateArtifactIds);
+ Callable cb =
+ createBuildCallable(rootSession, projectSegment, reactorContext, taskSegment, duplicateArtifactIds);
service.submit(cb);
}
@@ -160,12 +156,7 @@ private void multiThreadedProjectTaskSegmentBuild(
ProjectSegment scheduledDependent = projectBuildList.get(mavenProject);
logger.debug("Scheduling: {}", scheduledDependent);
Callable cb = createBuildCallable(
- rootSession,
- scheduledDependent,
- reactorContext,
- taskSegment,
- muxer,
- duplicateArtifactIds);
+ rootSession, scheduledDependent, reactorContext, taskSegment, duplicateArtifactIds);
service.submit(cb);
}
}
@@ -185,7 +176,6 @@ private Callable createBuildCallable(
final ProjectSegment projectBuild,
final ReactorContext reactorContext,
final TaskSegment taskSegment,
- final ThreadOutputMuxer muxer,
final Set duplicateArtifactIds) {
return () -> {
final Thread currentThread = Thread.currentThread();
@@ -198,10 +188,8 @@ private Callable createBuildCallable(
currentThread.setName("mvn-builder-" + threadNameSuffix);
try {
- // muxer.associateThreadWithProjectSegment( projectBuild );
lifecycleModuleBuilder.buildProject(
projectBuild.getSession(), rootSession, reactorContext, project, taskSegment);
- // muxer.setThisModuleComplete( projectBuild );
return projectBuild;
} finally {
diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/ThreadOutputMuxer.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/ThreadOutputMuxer.java
deleted file mode 100644
index 45ab841d0bc3..000000000000
--- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/multithreaded/ThreadOutputMuxer.java
+++ /dev/null
@@ -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;
-
-/**
- * NOTE: 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 projects;
-
- private final ThreadLocal projectBuildThreadLocal = new ThreadLocal<>();
-
- private final Map streams = new HashMap<>();
-
- private final Map printStreams = new HashMap<>();
-
- private final ByteArrayOutputStream defaultOutputStreamForUnknownData = new ByteArrayOutputStream();
-
- private final PrintStream defaultPrintStream = new PrintStream(defaultOutputStreamForUnknownData);
-
- private final Set 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();
- }
- }
- }
-}
diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
index d40e2e5ac47e..ce8efc8bd5be 100644
--- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
+++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
@@ -164,7 +164,6 @@ class BuildContext implements AutoCloseable {
final MavenSession session;
final ReactorContext reactorContext;
final PhasingExecutor executor;
- final ConcurrentLogOutput appender;
final Maporg.apache.maven
- maven-slf4j-wrapper
+ maven-loggingorg.apache.maven.resolver
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
index 180c03eb65b0..2a11634602d1 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
@@ -38,6 +38,7 @@
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.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 @@ void logging(CliRequest cliRequest) throws ExitException {
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());
}
}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
index dcc27022541b..e53e74bda617 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
@@ -32,8 +32,6 @@
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 ExecutionEventLogger(MessageBuilderFactory messageBuilderFactory, Logger
}
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 void sessionStarted(ExecutionEvent event) {
}
final List 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 void sessionEnded(ExecutionEvent event) {
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 void projectSkipped(ExecutionEvent event) {
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('-');
}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/logging/Slf4jLogger.java b/maven-embedder/src/main/java/org/apache/maven/cli/logging/Slf4jLogger.java
index 9cca0a940d1a..0dbce41f1263 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/logging/Slf4jLogger.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/logging/Slf4jLogger.java
@@ -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 @@
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 boolean isDebugEnabled() {
}
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 boolean isInfoEnabled() {
}
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 boolean isWarnEnabled() {
}
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 boolean isErrorEnabled() {
}
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 Logger getChildLogger(String name) {
public String getName() {
return logger.getName();
}
+
+ private void setMdc() {
+ if (projectId != null && ProjectBuildLogAppender.getProjectId() == null) {
+ ProjectBuildLogAppender.setProjectId(projectId);
+ }
+ }
}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/logging/impl/Slf4jSimpleConfiguration.java b/maven-embedder/src/main/java/org/apache/maven/cli/logging/impl/MavenSimpleConfiguration.java
similarity index 81%
rename from maven-embedder/src/main/java/org/apache/maven/cli/logging/impl/Slf4jSimpleConfiguration.java
rename to maven-embedder/src/main/java/org/apache/maven/cli/logging/impl/MavenSimpleConfiguration.java
index 42a85b0f4194..aa732397554c 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/logging/impl/Slf4jSimpleConfiguration.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/logging/impl/MavenSimpleConfiguration.java
@@ -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 void setRootLoggerLevel(Level level) {
@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();
+ }
}
}
diff --git a/maven-embedder/src/main/java/org/slf4j/simple/MavenSlf4jSimpleFriend.java b/maven-embedder/src/main/java/org/slf4j/simple/MavenSlf4jSimpleFriend.java
deleted file mode 100644
index e27d85f003e7..000000000000
--- a/maven-embedder/src/main/java/org/slf4j/simple/MavenSlf4jSimpleFriend.java
+++ /dev/null
@@ -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();
- }
- }
-}
diff --git a/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties b/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties
index 7431cb8f091d..ba7b675ea965 100644
--- a/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties
+++ b/maven-embedder/src/main/resources/META-INF/maven/slf4j-configuration.properties
@@ -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
diff --git a/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java
index 164d4bee84c4..43a75aa3239f 100644
--- a/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java
+++ b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java
@@ -98,4 +98,8 @@ public static int getTerminalWidth() {
public static MessageBuilder builder() {
return messageBuilderFactory.builder();
}
+
+ public static Terminal getTerminal() {
+ return terminal;
+ }
}
diff --git a/maven-slf4j-wrapper/pom.xml b/maven-logging/pom.xml
similarity index 74%
rename from maven-slf4j-wrapper/pom.xml
rename to maven-logging/pom.xml
index 6fa47292aa51..8b6cd681c976 100644
--- a/maven-slf4j-wrapper/pom.xml
+++ b/maven-logging/pom.xml
@@ -19,19 +19,25 @@ under the License.
-->
4.0.0
-
org.apache.mavenmaven4.0.0-beta-5-SNAPSHOT
- maven-slf4j-wrapper
-
- Maven SLF4J Wrapper
- This modules provides an ILoggerFactory interface which avoids a cyclic dependency between maven-embedder and maven-slf4j-provider.
+ maven-logging
+ Maven Logging
+ Provides the Maven Logging infrastructure
+
+ org.apache.maven
+ maven-api-core
+
+
+ org.apache.maven
+ maven-jline
+ org.slf4jslf4j-api
@@ -48,4 +54,14 @@ under the License.
test
+
+
+
+
+ org.eclipse.sisu
+ sisu-maven-plugin
+
+
+
+
diff --git a/maven-embedder/src/main/java/org/slf4j/MavenSlf4jFriend.java b/maven-logging/src/main/java/org/apache/maven/logging/api/LogLevelRecorder.java
similarity index 71%
rename from maven-embedder/src/main/java/org/slf4j/MavenSlf4jFriend.java
rename to maven-logging/src/main/java/org/apache/maven/logging/api/LogLevelRecorder.java
index b729879b0e3b..25ccc266e229 100644
--- a/maven-embedder/src/main/java/org/slf4j/MavenSlf4jFriend.java
+++ b/maven-logging/src/main/java/org/apache/maven/logging/api/LogLevelRecorder.java
@@ -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);
}
diff --git a/maven-logging/src/main/java/org/apache/maven/slf4j/DefaultLogLevelRecorder.java b/maven-logging/src/main/java/org/apache/maven/slf4j/DefaultLogLevelRecorder.java
new file mode 100644
index 000000000000..b1b9b7de668e
--- /dev/null
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/DefaultLogLevelRecorder.java
@@ -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 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 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();
+ }
+}
diff --git a/maven-logging/src/main/java/org/apache/maven/slf4j/MavenBaseLogger.java b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenBaseLogger.java
new file mode 100644
index 000000000000..273f13ebc72c
--- /dev/null
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenBaseLogger.java
@@ -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;
+
+/**
+ *
+ * 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:
+ *
+ *
+ *
+ *
org.slf4j.simpleLogger.logFile - The output target which can
+ * be the path to a file, or the special values "System.out" and
+ * "System.err". Default is "System.err".
+ *
+ *
org.slf4j.simpleLogger.cacheOutputStream - 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
+ * System.out/err 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
+ * System.out/err.
+ *
+ *
org.slf4j.simpleLogger.defaultLogLevel - 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".
+ *
+ *
org.slf4j.simpleLogger.log.a.b.c - 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
+ * org.slf4j.simpleLogger.defaultLogLevel will be used.
+ *
+ *
org.slf4j.simpleLogger.showDateTime - Set to
+ * true if you want the current date and time to be included in
+ * output messages. Default is false
+ *
+ *
org.slf4j.simpleLogger.dateTimeFormat - The date and time
+ * format to be used in the output messages. The pattern describing the date and
+ * time format is defined by
+ * SimpleDateFormat. If the format is not specified or is
+ * invalid, the number of milliseconds since start up will be output.
+ *
+ *
org.slf4j.simpleLogger.showThreadName -Set to
+ * true if you want to output the current thread name. Defaults to
+ * true.
+ *
+ *
(since version 1.7.33 and 2.0.0-alpha6) org.slf4j.simpleLogger.showThreadId -
+ * If you would like to output the current thread id, then set to
+ * true. Defaults to false.
+ *
+ *
org.slf4j.simpleLogger.showLogName - Set to
+ * true if you want the Logger instance name to be included in
+ * output messages. Defaults to true.
+ *
+ *
org.slf4j.simpleLogger.showShortLogName - Set to
+ * true if you want the last component of the name to be included
+ * in output messages. Defaults to false.
+ *
+ *
org.slf4j.simpleLogger.levelInBrackets - Should the level
+ * string be output in brackets? Defaults to false.
+ *
+ *
org.slf4j.simpleLogger.warnLevelString - The string value
+ * output for the warn level. Defaults to WARN.
+ *
+ *
+ *
+ *
+ * In addition to looking for system properties with the names specified above,
+ * this implementation also checks for a class loader resource named
+ * "simplelogger.properties", and includes any matching definitions
+ * from this resource (if it exists).
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ * Sample output follows.
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * This implementation is heavily inspired by
+ * Apache Commons Logging'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 SimpleLogger 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 markers = null;
+
+ if (marker != null) {
+ markers = new ArrayList<>();
+ markers.add(marker);
+ }
+
+ innerHandleNormalizedLoggingCall(level, markers, messagePattern, arguments, throwable);
+ }
+
+ private void innerHandleNormalizedLoggingCall(
+ Level level, List 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;
+ }
+}
diff --git a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenFailOnSeverityLogger.java b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenFailOnSeverityLogger.java
similarity index 95%
rename from maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenFailOnSeverityLogger.java
rename to maven-logging/src/main/java/org/apache/maven/slf4j/MavenFailOnSeverityLogger.java
index 65b317314047..8f2e7c6d3447 100644
--- a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenFailOnSeverityLogger.java
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenFailOnSeverityLogger.java
@@ -18,7 +18,6 @@
*/
package org.apache.maven.slf4j;
-import org.apache.maven.logwrapper.LogLevelRecorder;
import org.slf4j.event.Level;
/**
@@ -26,9 +25,9 @@
* 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;
}
diff --git a/maven-logging/src/main/java/org/apache/maven/slf4j/MavenLoggerFactory.java b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenLoggerFactory.java
new file mode 100644
index 000000000000..9ea6c18bc412
--- /dev/null
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenLoggerFactory.java
@@ -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 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);
+ }
+ });
+ }
+}
diff --git a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenServiceProvider.java b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenServiceProvider.java
similarity index 84%
rename from maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenServiceProvider.java
rename to maven-logging/src/main/java/org/apache/maven/slf4j/MavenServiceProvider.java
index 11e2f9371506..af7fe1ca6521 100644
--- a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenServiceProvider.java
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenServiceProvider.java
@@ -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;
diff --git a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java
similarity index 80%
rename from maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java
rename to maven-logging/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java
index 749d60de883a..14b81aff4923 100644
--- a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java
@@ -22,7 +22,6 @@
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 @@
*
* @since 3.5.0
*/
-public class MavenSimpleLogger extends ExtSimpleLogger {
+public class MavenSimpleLogger extends MavenBaseLogger {
private String traceRenderedLevel;
private String debugRenderedLevel;
@@ -74,18 +73,24 @@ protected String renderLevel(int level) {
}
}
- @Override
- protected void doWrite(StringBuilder buf, Throwable t) {
+ protected void write(StringBuilder buf, Throwable t) {
Consumer 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 stream) {
if (t == null) {
return;
}
@@ -93,12 +98,12 @@ protected void writeThrowable(Throwable t, PrintStream stream) {
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 stream, String prefix) {
MessageBuilder builder = builder();
for (StackTraceElement e : t.getStackTrace()) {
builder.a(prefix);
@@ -111,7 +116,7 @@ private void printStackTrace(Throwable t, PrintStream stream, String prefix) {
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 @@ private void printStackTrace(Throwable t, PrintStream stream, String prefix) {
}
}
- private void writeThrowable(Throwable t, PrintStream stream, String caption, String prefix) {
+ protected void writeThrowable(Throwable t, Consumer 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 @@ protected String getLocation(final StackTraceElement e) {
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;
+ }
}
diff --git a/maven-logging/src/main/java/org/apache/maven/slf4j/OutputChoice.java b/maven-logging/src/main/java/org/apache/maven/slf4j/OutputChoice.java
new file mode 100644
index 000000000000..fa491b596dca
--- /dev/null
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/OutputChoice.java
@@ -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();
+ }
+ }
+}
diff --git a/maven-logging/src/main/java/org/apache/maven/slf4j/SimpleLoggerConfiguration.java b/maven-logging/src/main/java/org/apache/maven/slf4j/SimpleLoggerConfiguration.java
new file mode 100644
index 000000000000..02ca0f464e2a
--- /dev/null
+++ b/maven-logging/src/main/java/org/apache/maven/slf4j/SimpleLoggerConfiguration.java
@@ -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);
+ }
+ }
+ }
+}
diff --git a/maven-slf4j-provider/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/maven-logging/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider
similarity index 100%
rename from maven-slf4j-provider/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider
rename to maven-logging/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider
diff --git a/maven-slf4j-provider/src/site/apt/index.apt b/maven-logging/src/site/apt/index.apt
similarity index 100%
rename from maven-slf4j-provider/src/site/apt/index.apt
rename to maven-logging/src/site/apt/index.apt
diff --git a/maven-slf4j-provider/src/site/site.xml b/maven-logging/src/site/site.xml
similarity index 100%
rename from maven-slf4j-provider/src/site/site.xml
rename to maven-logging/src/site/site.xml
diff --git a/maven-slf4j-wrapper/src/test/java/org/apache/maven/logwrapper/LogLevelRecorderTest.java b/maven-logging/src/test/java/org/apache/maven/slf4j/LogLevelRecorderTest.java
similarity index 84%
rename from maven-slf4j-wrapper/src/test/java/org/apache/maven/logwrapper/LogLevelRecorderTest.java
rename to maven-logging/src/test/java/org/apache/maven/slf4j/LogLevelRecorderTest.java
index e806fb620799..264be86f635b 100644
--- a/maven-slf4j-wrapper/src/test/java/org/apache/maven/logwrapper/LogLevelRecorderTest.java
+++ b/maven-logging/src/test/java/org/apache/maven/slf4j/LogLevelRecorderTest.java
@@ -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 @@
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 @@ void createsLogLevelRecorder() {
@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 @@ void createsLogLevelRecorderWithWarning() {
@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"));
diff --git a/maven-slf4j-provider/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java b/maven-logging/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java
similarity index 72%
rename from maven-slf4j-provider/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java
rename to maven-logging/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java
index 04475427d8e6..6ee870dc62cc 100644
--- a/maven-slf4j-provider/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java
+++ b/maven-logging/src/test/java/org/apache/maven/slf4j/MavenLoggerFactoryTest.java
@@ -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.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 @@ void loggerCachingWorks() {
@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());
}
}
diff --git a/maven-slf4j-provider/src/test/java/org/apache/maven/slf4j/MavenSimpleLoggerTest.java b/maven-logging/src/test/java/org/apache/maven/slf4j/MavenSimpleLoggerTest.java
similarity index 97%
rename from maven-slf4j-provider/src/test/java/org/apache/maven/slf4j/MavenSimpleLoggerTest.java
rename to maven-logging/src/test/java/org/apache/maven/slf4j/MavenSimpleLoggerTest.java
index bd2e1dd4a9ec..e26ea811830d 100644
--- a/maven-slf4j-provider/src/test/java/org/apache/maven/slf4j/MavenSimpleLoggerTest.java
+++ b/maven-logging/src/test/java/org/apache/maven/slf4j/MavenSimpleLoggerTest.java
@@ -49,7 +49,7 @@ void tearDown() {
}
@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 @@ void includesCauseAndSuppressedExceptionsWhenWritingThrowables(TestInfo testInfo
new MavenSimpleLogger("logger").writeThrowable(throwable, new PrintStream(output));
- String actual = output.toString(UTF_8.name());
+ String actual = output.toString(UTF_8);
List actualLines = Arrays.asList(actual.split(System.lineSeparator()));
Class> testClass = testInfo.getTestClass().get();
diff --git a/maven-slf4j-provider/pom.xml b/maven-slf4j-provider/pom.xml
deleted file mode 100644
index 40a6091e27d3..000000000000
--- a/maven-slf4j-provider/pom.xml
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
- 4.0.0
-
-
- org.apache.maven
- maven
- 4.0.0-beta-5-SNAPSHOT
-
-
- maven-slf4j-provider
-
- Maven SLF4J Simple Provider
- Maven SLF4J provider based on SLF4J's simple provider, extended to support Maven styled colors
- for levels and stacktrace rendering.
-
-
-
- org.slf4j
- slf4j-api
-
-
- org.apache.maven
- maven-slf4j-wrapper
-
-
- org.apache.maven
- maven-api-core
-
-
- org.apache.maven
- maven-jline
-
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
- org.hamcrest
- hamcrest
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
-
- org.slf4j
- slf4j-simple
- ${slf4jVersion}
- jar
- sources
- false
- ${project.build.directory}/generated-sources/slf4j-simple
- org/slf4j/simple/*.java
-
-
-
-
-
- unzip-slf4j-simple
-
- unpack
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
-
-
- add-slf4j-simple
-
- add-source
-
- generate-sources
-
-
-
-
-
-
-
-
-
-
-
diff --git a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenLoggerFactory.java b/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenLoggerFactory.java
deleted file mode 100644
index ea0b98272bcc..000000000000
--- a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenLoggerFactory.java
+++ /dev/null
@@ -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 getLogLevelRecorder() {
- return Optional.ofNullable(logLevelRecorder);
- }
-
- protected Logger createLogger(String name) {
- if (logLevelRecorder == null) {
- return new MavenSimpleLogger(name);
- } else {
- return new MavenFailOnSeverityLogger(name, logLevelRecorder);
- }
- }
-}
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/simple/ExtSimpleLogger.java b/maven-slf4j-provider/src/main/java/org/slf4j/simple/ExtSimpleLogger.java
deleted file mode 100644
index 973f75a7e823..000000000000
--- a/maven-slf4j-provider/src/main/java/org/slf4j/simple/ExtSimpleLogger.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/maven-slf4j-wrapper/src/main/java/org/apache/maven/logwrapper/LogLevelRecorder.java b/maven-slf4j-wrapper/src/main/java/org/apache/maven/logwrapper/LogLevelRecorder.java
deleted file mode 100644
index d77ca4df1f51..000000000000
--- a/maven-slf4j-wrapper/src/main/java/org/apache/maven/logwrapper/LogLevelRecorder.java
+++ /dev/null
@@ -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 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;
- }
-}
diff --git a/maven-slf4j-wrapper/src/site/site.xml b/maven-slf4j-wrapper/src/site/site.xml
deleted file mode 100644
index 8ffe43d07c30..000000000000
--- a/maven-slf4j-wrapper/src/site/site.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
- ${project.scm.url}
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 1526eb14d32b..8f6352e71ddb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -109,14 +109,13 @@ under the License.
maven-dimaven-xml-implmaven-jline
+ maven-loggingmaven-coremaven-settingsmaven-settings-buildermaven-artifactmaven-resolver-providermaven-repository-metadata
- maven-slf4j-provider
- maven-slf4j-wrappermaven-embeddermaven-climaven-compat
@@ -216,6 +215,11 @@ under the License.
maven-jline${project.version}