diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java index f2968295e456..a0cd78fd39d4 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/Log.java @@ -36,6 +36,58 @@ @Experimental @Provider public interface Log { + /** + * {@return true if the trace error level is enabled} + */ + boolean isTraceEnabled(); + + /** + * Sends a message to the user in the trace error level. + *

+ * Trace is the most verbose level, intended for Maven core internals + * such as resolver negotiation, model interpolation, and lifecycle + * ordering details. Use {@link #debug(CharSequence)} instead for + * messages that help users investigate their build + * (e.g. why a module was recompiled). + * + * @param content the message to log + */ + void trace(CharSequence content); + + /** + * Sends a message (and accompanying exception) to the user at the trace error level. + * The error's stacktrace will be output when this error level is enabled. + * + * @param content the message to log + * @param error the error that caused this log + */ + void trace(CharSequence content, Throwable error); + + /** + * Sends an exception to the user in the trace error level. + * The stack trace for this exception will be output when this error level is enabled. + * + * @param error the error that caused this log + */ + void trace(Throwable error); + + /** + * Sends a lazily-computed message in the trace error level. + * The supplier is only evaluated if trace is enabled. + * + * @param content the message supplier + */ + void trace(Supplier content); + + /** + * Sends a lazily-computed message (and accompanying exception) in the trace error level. + * The supplier is only evaluated if trace is enabled. + * + * @param content the message supplier + * @param error the error that caused this log + */ + void trace(Supplier content, Throwable error); + /** * {@return true if the debug error level is enabled} */ @@ -43,6 +95,11 @@ public interface Log { /** * Sends a message to the user in the debug error level. + *

+ * Debug is intended for messages that help users investigate + * their build — for example, why a module was recompiled or what + * classpath was resolved. For Maven core internals, use + * {@link #trace(CharSequence)} instead. * * @param content the message to log */ @@ -167,4 +224,27 @@ public interface Log { void error(Supplier content); void error(Supplier content, Throwable error); + + /** + * Returns a child logger whose name is derived from this logger's name + * by appending a dot and the given suffix. + * + *

For example, if a plugin's logger is named + * {@code "org.apache.maven.plugins.compiler.CompilerMojo"}, + * then {@code child("diagnostics")} returns a logger named + * {@code "org.apache.maven.plugins.compiler.CompilerMojo.diagnostics"}. + * This lets sub-components log under an independently filterable name + * without requiring a separate injection point.

+ * + *

The default implementation returns {@code this}, so existing + * {@code Log} implementations continue to work without changes. + * Implementations that wrap a hierarchical logging backend (such as + * SLF4J) should override this to create a real child logger.

+ * + * @param name the suffix to append (must not be {@code null} or blank) + * @return a child logger — never {@code null} + */ + default Log child(String name) { + return this; + } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLog.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLog.java index 1a11fe46fdb5..b1cf40cc4059 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLog.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLog.java @@ -22,6 +22,7 @@ import org.apache.maven.api.plugin.Log; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static java.util.Objects.requireNonNull; @@ -32,6 +33,46 @@ public DefaultLog(Logger logger) { this.logger = requireNonNull(logger); } + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public void trace(CharSequence content) { + if (isTraceEnabled()) { + logger.trace(toString(content)); + } + } + + @Override + public void trace(CharSequence content, Throwable error) { + if (isTraceEnabled()) { + logger.trace(toString(content), error); + } + } + + @Override + public void trace(Throwable error) { + if (isTraceEnabled()) { + logger.trace("", error); + } + } + + @Override + public void trace(Supplier content) { + if (isTraceEnabled()) { + logger.trace(content.get()); + } + } + + @Override + public void trace(Supplier content, Throwable error) { + if (isTraceEnabled()) { + logger.trace(content.get(), error); + } + } + @Override public void debug(CharSequence content) { if (isDebugEnabled()) { @@ -48,7 +89,9 @@ public void debug(CharSequence content, Throwable error) { @Override public void debug(Throwable error) { - logger.debug("", error); + if (isDebugEnabled()) { + logger.debug("", error); + } } @Override @@ -81,7 +124,9 @@ public void info(CharSequence content, Throwable error) { @Override public void info(Throwable error) { - logger.info("", error); + if (isInfoEnabled()) { + logger.info("", error); + } } @Override @@ -114,7 +159,9 @@ public void warn(CharSequence content, Throwable error) { @Override public void warn(Throwable error) { - logger.warn("", error); + if (isWarnEnabled()) { + logger.warn("", error); + } } @Override @@ -127,7 +174,7 @@ public void warn(Supplier content) { @Override public void warn(Supplier content, Throwable error) { if (isWarnEnabled()) { - logger.info(content.get(), error); + logger.warn(content.get(), error); } } @@ -147,7 +194,9 @@ public void error(CharSequence content, Throwable error) { @Override public void error(Throwable error) { - logger.error("", error); + if (isErrorEnabled()) { + logger.error("", error); + } } @Override @@ -184,6 +233,12 @@ public boolean isErrorEnabled() { return logger.isErrorEnabled(); } + @Override + public Log child(String name) { + requireNonNull(name, "name"); + return new DefaultLog(LoggerFactory.getLogger(logger.getName() + "." + name)); + } + private String toString(CharSequence content) { return content != null ? content.toString() : ""; } diff --git a/impl/maven-core/src/main/java/org/apache/maven/logging/LoggingExecutionListener.java b/impl/maven-core/src/main/java/org/apache/maven/logging/LoggingExecutionListener.java index 040a481455e2..6685351b362a 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/logging/LoggingExecutionListener.java +++ b/impl/maven-core/src/main/java/org/apache/maven/logging/LoggingExecutionListener.java @@ -121,6 +121,7 @@ public void projectSkipped(ExecutionEvent event) { @Override public void mojoStarted(ExecutionEvent event) { setMdc(event); + setMojoMdc(event); buildEventListener.mojoStarted(event); delegate.mojoStarted(event); } @@ -128,12 +129,14 @@ public void mojoStarted(ExecutionEvent event) { @Override public void mojoSucceeded(ExecutionEvent event) { setMdc(event); + ProjectBuildLogAppender.setMojoId(null); delegate.mojoSucceeded(event); } @Override public void mojoFailed(ExecutionEvent event) { setMdc(event); + ProjectBuildLogAppender.setMojoId(null); delegate.mojoFailed(event); } @@ -187,4 +190,12 @@ private void setMdc(ExecutionEvent event) { ProjectBuildLogAppender.setProjectId(event.getProject().getArtifactId()); } } + + private void setMojoMdc(ExecutionEvent event) { + if (event.getMojoExecution() != null) { + String mojoId = event.getMojoExecution().getMojoDescriptor().getFullGoalName() + "@" + + event.getMojoExecution().getExecutionId(); + ProjectBuildLogAppender.setMojoId(mojoId); + } + } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/logging/ProjectBuildLogAppender.java b/impl/maven-core/src/main/java/org/apache/maven/logging/ProjectBuildLogAppender.java index 8465df0cf060..947d09474f21 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/logging/ProjectBuildLogAppender.java +++ b/impl/maven-core/src/main/java/org/apache/maven/logging/ProjectBuildLogAppender.java @@ -27,7 +27,9 @@ public class ProjectBuildLogAppender implements AutoCloseable { private static final String KEY_PROJECT_ID = "maven.project.id"; + private static final String KEY_MOJO_ID = "maven.mojo.id"; private static final ThreadLocal PROJECT_ID = new InheritableThreadLocal<>(); + private static final ThreadLocal MOJO_ID = new InheritableThreadLocal<>(); private static final ThreadLocal FORKING_PROJECT_ID = new InheritableThreadLocal<>(); public static String getProjectId() { @@ -52,6 +54,31 @@ public static void setProjectId(String projectId) { } } + public static String getMojoId() { + return MOJO_ID.get(); + } + + /** + * Sets or clears the mojo execution identifier in both the thread-local + * and the SLF4J MDC. The value is available to any SLF4J appender via + * the MDC key {@code maven.mojo.id} and to JUL-bridged messages through + * the same MDC path. + *

+ * Format: {@code "prefix:goal@executionId"} + * (e.g. {@code "compiler:compile@default-compile"}). + * + * @param mojoId the mojo identifier, or {@code null} to clear + */ + public static void setMojoId(String mojoId) { + if (mojoId != null) { + MOJO_ID.set(mojoId); + MDC.put(KEY_MOJO_ID, mojoId); + } else { + MOJO_ID.remove(); + MDC.remove(KEY_MOJO_ID); + } + } + public static void setForkingProjectId(String forkingProjectId) { if (forkingProjectId != null) { FORKING_PROJECT_ID.set(forkingProjectId); diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java index e395d1ed000b..476f2aadcdd3 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java @@ -125,7 +125,7 @@ public void executeMojo(MavenSession session, MojoExecution mojoExecution) scope.seed( org.apache.maven.api.plugin.Log.class, new DefaultLog(LoggerFactory.getLogger( - mojoExecution.getMojoDescriptor().getFullGoalName()))); + mojoExecution.getMojoDescriptor().getImplementation()))); InternalMavenSession sessionV4 = InternalMavenSession.from(session.getSession()); scope.seed(Project.class, sessionV4.getProject(project)); scope.seed(org.apache.maven.api.MojoExecution.class, new DefaultMojoExecution(sessionV4, mojoExecution)); diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 15f3d8df7b64..62a39f72fb8c 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -555,7 +555,7 @@ private T loadV4Mojo( org.apache.maven.api.MojoExecution execution = new DefaultMojoExecution(sessionV4, mojoExecution); org.apache.maven.api.plugin.Log log = new DefaultLog( - LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getFullGoalName())); + LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getImplementation())); try { Injector injector = Injector.create(); injector.discover(pluginRealm);