gnodet-bot commented on code in PR #13695:
URL: https://github.com/apache/maven/pull/13695#discussion_r4365530540


##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLogger.java:
##########
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.StackWalker.StackFrame;
+import java.time.Instant;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+import org.apache.maven.api.MonotonicClock;
+import org.apache.maven.api.logging.LogEvent;
+import org.apache.maven.api.logging.LogEventBuilder;
+import org.apache.maven.api.logging.LogLevel;
+import org.apache.maven.api.logging.LogSink;
+import org.apache.maven.api.logging.Logger;
+import org.apache.maven.api.logging.LoggerManager;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Default implementation of both {@link Logger} (new fluent API)
+ * and the deprecated {@link org.apache.maven.api.plugin.Log} (backward 
compatibility).
+ * <p>
+ * Builds fully-structured {@link LogEvent} objects — including {@code 
projectId},
+ * {@code mojoId}, timestamp, and sequence number — and dispatches them to the
+ * current {@link LogSink} obtained from {@link LoggerManager#get()}.
+ * Level checks and sink lookup both go through the {@code LoggerManager},
+ * so the sink can be changed at runtime.
+ *
+ * @since 4.0.0
+ */
+@SuppressWarnings("deprecation")
+public class DefaultLogger implements Logger, org.apache.maven.api.plugin.Log {
+
+    private static final StackWalker WALKER = StackWalker.getInstance();
+    private static final String THIS_CLASS = DefaultLogger.class.getName();
+    private static final String BUILDER_CLASS = 
DefaultLogEventBuilder.class.getName();
+
+    private static final AtomicLong SEQUENCE = new AtomicLong();
+
+    private final String name;
+    private final String projectId;
+    private final String mojoId;
+
+    public DefaultLogger(String name) {
+        this(name, null, null);
+    }
+
+    public DefaultLogger(String name, String projectId, String mojoId) {
+        this.name = requireNonNull(name, "name");
+        this.projectId = projectId;
+        this.mojoId = mojoId;
+    }
+
+    // ---- org.apache.maven.api.logging.Logger ----
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    private boolean isLevelEnabled(LogLevel level) {
+        LoggerManager mgr = LoggerManager.get();
+        return mgr != null && mgr.isLevelEnabled(name, level);
+    }
+
+    @Override
+    public boolean isEnabled(LogLevel level) {
+        return isLevelEnabled(level);
+    }
+
+    @Override
+    public LogEventBuilder atTrace() {
+        return isLevelEnabled(LogLevel.TRACE)
+                ? new DefaultLogEventBuilder(this, LogLevel.TRACE)
+                : NopLogEventBuilder.INSTANCE;
+    }
+
+    @Override
+    public LogEventBuilder atDebug() {
+        return isLevelEnabled(LogLevel.DEBUG)
+                ? new DefaultLogEventBuilder(this, LogLevel.DEBUG)
+                : NopLogEventBuilder.INSTANCE;
+    }
+
+    @Override
+    public LogEventBuilder atInfo() {
+        return isLevelEnabled(LogLevel.INFO)
+                ? new DefaultLogEventBuilder(this, LogLevel.INFO)
+                : NopLogEventBuilder.INSTANCE;
+    }
+
+    @Override
+    public LogEventBuilder atWarn() {
+        return isLevelEnabled(LogLevel.WARN)
+                ? new DefaultLogEventBuilder(this, LogLevel.WARN)
+                : NopLogEventBuilder.INSTANCE;
+    }
+
+    @Override
+    public LogEventBuilder atError() {
+        return isLevelEnabled(LogLevel.ERROR)
+                ? new DefaultLogEventBuilder(this, LogLevel.ERROR)
+                : NopLogEventBuilder.INSTANCE;
+    }
+
+    @Override
+    public LogEventBuilder at(LogLevel level) {
+        return isLevelEnabled(level) ? new DefaultLogEventBuilder(this, level) 
: NopLogEventBuilder.INSTANCE;
+    }
+
+    @Override
+    public DefaultLogger child(String childName) {
+        requireNonNull(childName, "childName");
+        return new DefaultLogger(name + "." + childName, projectId, mojoId);
+    }
+
+    // ---- Internal dispatch ----
+
+    void doLog(LogLevel level, String message, Throwable throwable) {
+        doLog(level, message, null, null, null, throwable);
+    }
+
+    @SuppressWarnings("deprecation") // Thread.getId()
+    void doLog(
+            LogLevel level,
+            String message,
+            String messageTemplate,
+            Object[] messageArgs,
+            Map<String, String> keyValues,
+            Throwable throwable) {
+        LoggerManager mgr = LoggerManager.get();
+        if (mgr == null) {
+            return;
+        }
+        LogSink sink = mgr.getLogSink();
+        if (sink == null) {
+            return;
+        }
+
+        Instant timestamp = MonotonicClock.now();
+        long seq = SEQUENCE.getAndIncrement();
+
+        String sourceClass = null;
+        String sourceMethod = null;
+        long threadId = -1;
+        String threadName = null;
+
+        if (sink.needsMetadata()) {
+            sourceClass = name;
+            sourceMethod = resolveCallerMethod();
+            threadId = Thread.currentThread().getId();
+            threadName = Thread.currentThread().getName();
+        }
+
+        String stackTrace = throwable != null ? formatStackTrace(throwable) : 
null;
+
+        LogEvent event = new DefaultLogEvent(
+                timestamp,
+                level,
+                message,
+                name,
+                stackTrace,
+                throwable,
+                sourceClass,
+                sourceMethod,
+                threadId,
+                seq,
+                threadName,
+                projectId,
+                mojoId,
+                keyValues,
+                messageTemplate,
+                messageArgs);
+
+        sink.accept(event);

Review Comment:
   🔴 **[Not addressed] Bug: `doLog()` never calls `record()` — 
`--fail-on-severity` broken for new `Logger` API.**
   
   `sink.accept(event)` dispatches the event but never calls 
`DefaultLoggerManager.record(level)`. The old `MavenFailOnSeverityLogger` 
(deleted in this PR) called `logLevelRecorder.record()` from the SLF4J dispatch 
path. The new `MavenFailOnSeverityLogger` in `maven-logging-slf4j` still calls 
`dlm.record(level)` but only covers the SLF4J path. Any plugin using the new 
fluent `Logger` API will silently bypass `--fail-on-severity`.
   
   ```suggestion
           sink.accept(event);
           if (mgr instanceof DefaultLoggerManager dlm) {
               dlm.record(level);
           }
   ```



##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLoggerManager.java:
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.logging.LogEvent;
+import org.apache.maven.api.logging.LogLevel;
+import org.apache.maven.api.logging.LogSink;
+import org.apache.maven.api.logging.Logger;
+import org.apache.maven.api.logging.LoggerManager;
+
+/**
+ * Default {@link LoggerManager} implementation. Owns:
+ * <ul>
+ *   <li>Per-logger level configuration (loaded from properties files)</li>
+ *   <li>The active {@link LogSink}</li>
+ *   <li>Severity tracking for {@code --fail-on-severity}</li>
+ * </ul>
+ * <p>
+ * This class has no SLF4J dependency. The SLF4J adapter layer creates
+ * an instance and registers it via {@link LoggerManager#set(LoggerManager)}.
+ */
+public class DefaultLoggerManager implements LoggerManager {
+
+    private static final String CONFIGURATION_FILE = "maven.logger.properties";
+
+    @Deprecated(since = "4.0.0")
+    private static final String LEGACY_CONFIGURATION_FILE = 
"simplelogger.properties";
+
+    private static final String LEGACY_PREFIX = "org.slf4j.simpleLogger.";
+
+    public static final int LOG_LEVEL_TRACE = 0;
+    public static final int LOG_LEVEL_DEBUG = 10;
+    public static final int LOG_LEVEL_INFO = 20;
+    public static final int LOG_LEVEL_WARN = 30;
+    public static final int LOG_LEVEL_ERROR = 40;
+    public static final int LOG_LEVEL_OFF = 50;
+
+    private volatile LogSink logSink;
+    private volatile int defaultLogLevel = LOG_LEVEL_INFO;
+    private final AtomicReference<LogLevel> maxReached = new 
AtomicReference<>(LogLevel.DEBUG);
+    private final Properties properties = new Properties();
+    private final ConcurrentHashMap<String, Integer> levelCache = new 
ConcurrentHashMap<>();
+
+    public DefaultLoggerManager() {
+        this.logSink = new AccumulatingLogSink();
+        LoggerManager.set(this);
+        loadConfiguration();
+    }
+
+    // -- LogManager: loggers --
+
+    @Override
+    public Logger getLogger(String name) {
+        return new DefaultLogger(name);
+    }
+
+    // -- LogManager: sink --
+
+    @Override
+    public LogSink getLogSink() {
+        return logSink;
+    }
+
+    @Override
+    public void setLogSink(LogSink sink) {
+        LogSink old = this.logSink;
+        this.logSink = sink;
+        if (old instanceof AccumulatingLogSink acc && sink != null) {
+            acc.drainTo(sink);

Review Comment:
   🔴 **[Not addressed] Race condition: new sink becomes visible before old 
events are drained.**
   
   `this.logSink = sink` at L91 makes the new sink immediately visible to 
concurrent `doLog()` callers. `drainTo()` at L93 then replays buffered events. 
Between L91 and L93, concurrent loggers write directly to the real sink while 
the drain is still replaying older events — breaking ordering guarantees. 
Additionally, `CopyOnWriteArrayList.clear()` in `drainTo()` can discard events 
added concurrently between the iteration completing and `clear()` executing.
   
   Fix: drain first, then swap:
   ```suggestion
       public void setLogSink(LogSink sink) {
           LogSink old = this.logSink;
           if (old instanceof AccumulatingLogSink acc && sink != null) {
               acc.drainTo(sink);
           }
           this.logSink = sink;
       }
   ```



##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLoggerManager.java:
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.logging.LogEvent;
+import org.apache.maven.api.logging.LogLevel;
+import org.apache.maven.api.logging.LogSink;
+import org.apache.maven.api.logging.Logger;
+import org.apache.maven.api.logging.LoggerManager;
+
+/**
+ * Default {@link LoggerManager} implementation. Owns:
+ * <ul>
+ *   <li>Per-logger level configuration (loaded from properties files)</li>
+ *   <li>The active {@link LogSink}</li>
+ *   <li>Severity tracking for {@code --fail-on-severity}</li>
+ * </ul>
+ * <p>
+ * This class has no SLF4J dependency. The SLF4J adapter layer creates
+ * an instance and registers it via {@link LoggerManager#set(LoggerManager)}.
+ */
+public class DefaultLoggerManager implements LoggerManager {
+
+    private static final String CONFIGURATION_FILE = "maven.logger.properties";
+
+    @Deprecated(since = "4.0.0")
+    private static final String LEGACY_CONFIGURATION_FILE = 
"simplelogger.properties";
+
+    private static final String LEGACY_PREFIX = "org.slf4j.simpleLogger.";
+
+    public static final int LOG_LEVEL_TRACE = 0;
+    public static final int LOG_LEVEL_DEBUG = 10;
+    public static final int LOG_LEVEL_INFO = 20;
+    public static final int LOG_LEVEL_WARN = 30;
+    public static final int LOG_LEVEL_ERROR = 40;
+    public static final int LOG_LEVEL_OFF = 50;
+
+    private volatile LogSink logSink;
+    private volatile int defaultLogLevel = LOG_LEVEL_INFO;
+    private final AtomicReference<LogLevel> maxReached = new 
AtomicReference<>(LogLevel.DEBUG);
+    private final Properties properties = new Properties();
+    private final ConcurrentHashMap<String, Integer> levelCache = new 
ConcurrentHashMap<>();
+
+    public DefaultLoggerManager() {
+        this.logSink = new AccumulatingLogSink();
+        LoggerManager.set(this);
+        loadConfiguration();
+    }
+
+    // -- LogManager: loggers --
+
+    @Override
+    public Logger getLogger(String name) {
+        return new DefaultLogger(name);
+    }
+
+    // -- LogManager: sink --
+
+    @Override
+    public LogSink getLogSink() {
+        return logSink;
+    }
+
+    @Override
+    public void setLogSink(LogSink sink) {
+        LogSink old = this.logSink;
+        this.logSink = sink;
+        if (old instanceof AccumulatingLogSink acc && sink != null) {
+            acc.drainTo(sink);
+        }
+    }
+
+    /**
+     * A sink that buffers events until a real sink is installed.
+     * Used as the default during early bootstrap.
+     */
+    private static class AccumulatingLogSink implements LogSink {
+        private final List<LogEvent> events = new CopyOnWriteArrayList<>();
+
+        @Override
+        public void accept(LogEvent event) {
+            events.add(event);
+        }
+
+        void drainTo(LogSink target) {
+            for (LogEvent event : events) {
+                target.accept(event);
+            }
+            events.clear();
+        }
+    }
+
+    // -- LogManager: level checking --
+
+    @Override
+    public boolean isLevelEnabled(String loggerName, LogLevel level) {
+        int configuredLevel = getLoggerLevel(loggerName);
+        return toIntLevel(level) >= configuredLevel;
+    }
+
+    @Override
+    public void setRootLoggerLevel(LogLevel level) {
+        String value =
+                switch (level) {
+                    case TRACE, DEBUG -> "debug";
+                    case INFO -> "info";
+                    case WARN -> "warn";
+                    case ERROR -> "error";
+                };
+        System.setProperty(Constants.MAVEN_LOGGER_DEFAULT_LOG_LEVEL, value);

Review Comment:
   🔴 **New: `setRootLoggerLevel()` does not call `reconfigure()` — `levelCache` 
stays stale.**
   
   `setRootLoggerLevel()` writes the new level to a system property (L132) but 
does not clear `levelCache`. All loggers that already resolved their level 
continue at the stale cached value until the next `reconfigure()` call (or JVM 
restart). In practice, `-X` and `-q` are applied at startup before any logger 
levels are cached, so this may not manifest immediately — but it will break any 
runtime level changes.
   
   The Javadoc on the interface says "Takes effect after `reconfigure()`" which 
acknowledges the issue, but that contract puts the burden on callers who can't 
know the internals. `setRootLoggerLevel()` should be self-contained:
   
   ```suggestion
       @Override
       public void setRootLoggerLevel(LogLevel level) {
           String value =
                   switch (level) {
                       case TRACE -> "trace";
                       case DEBUG -> "debug";
                       case INFO -> "info";
                       case WARN -> "warn";
                       case ERROR -> "error";
                   };
           System.setProperty(Constants.MAVEN_LOGGER_DEFAULT_LOG_LEVEL, value);
           reconfigure();
       }
   ```



##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLoggerManager.java:
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.logging.LogEvent;
+import org.apache.maven.api.logging.LogLevel;
+import org.apache.maven.api.logging.LogSink;
+import org.apache.maven.api.logging.Logger;
+import org.apache.maven.api.logging.LoggerManager;
+
+/**
+ * Default {@link LoggerManager} implementation. Owns:
+ * <ul>
+ *   <li>Per-logger level configuration (loaded from properties files)</li>
+ *   <li>The active {@link LogSink}</li>
+ *   <li>Severity tracking for {@code --fail-on-severity}</li>
+ * </ul>
+ * <p>
+ * This class has no SLF4J dependency. The SLF4J adapter layer creates
+ * an instance and registers it via {@link LoggerManager#set(LoggerManager)}.
+ */
+public class DefaultLoggerManager implements LoggerManager {
+
+    private static final String CONFIGURATION_FILE = "maven.logger.properties";
+
+    @Deprecated(since = "4.0.0")
+    private static final String LEGACY_CONFIGURATION_FILE = 
"simplelogger.properties";
+
+    private static final String LEGACY_PREFIX = "org.slf4j.simpleLogger.";
+
+    public static final int LOG_LEVEL_TRACE = 0;
+    public static final int LOG_LEVEL_DEBUG = 10;
+    public static final int LOG_LEVEL_INFO = 20;
+    public static final int LOG_LEVEL_WARN = 30;
+    public static final int LOG_LEVEL_ERROR = 40;
+    public static final int LOG_LEVEL_OFF = 50;
+
+    private volatile LogSink logSink;
+    private volatile int defaultLogLevel = LOG_LEVEL_INFO;
+    private final AtomicReference<LogLevel> maxReached = new 
AtomicReference<>(LogLevel.DEBUG);
+    private final Properties properties = new Properties();
+    private final ConcurrentHashMap<String, Integer> levelCache = new 
ConcurrentHashMap<>();
+
+    public DefaultLoggerManager() {
+        this.logSink = new AccumulatingLogSink();
+        LoggerManager.set(this);
+        loadConfiguration();
+    }
+
+    // -- LogManager: loggers --
+
+    @Override
+    public Logger getLogger(String name) {
+        return new DefaultLogger(name);
+    }
+
+    // -- LogManager: sink --
+
+    @Override
+    public LogSink getLogSink() {
+        return logSink;
+    }
+
+    @Override
+    public void setLogSink(LogSink sink) {
+        LogSink old = this.logSink;
+        this.logSink = sink;
+        if (old instanceof AccumulatingLogSink acc && sink != null) {
+            acc.drainTo(sink);
+        }
+    }
+
+    /**
+     * A sink that buffers events until a real sink is installed.
+     * Used as the default during early bootstrap.
+     */
+    private static class AccumulatingLogSink implements LogSink {
+        private final List<LogEvent> events = new CopyOnWriteArrayList<>();
+
+        @Override
+        public void accept(LogEvent event) {
+            events.add(event);
+        }
+
+        void drainTo(LogSink target) {
+            for (LogEvent event : events) {
+                target.accept(event);
+            }
+            events.clear();
+        }
+    }
+
+    // -- LogManager: level checking --
+
+    @Override
+    public boolean isLevelEnabled(String loggerName, LogLevel level) {
+        int configuredLevel = getLoggerLevel(loggerName);
+        return toIntLevel(level) >= configuredLevel;
+    }
+
+    @Override
+    public void setRootLoggerLevel(LogLevel level) {
+        String value =
+                switch (level) {
+                    case TRACE, DEBUG -> "debug";

Review Comment:
   🔴 **[Not addressed] `setRootLoggerLevel(TRACE)` silently maps to `"debug"` — 
TRACE logging unreachable.**
   
   `case TRACE, DEBUG -> "debug"` means `setRootLoggerLevel(LogLevel.TRACE)` 
writes `"debug"` to the system property. `stringToLevel("debug")` returns 
`LOG_LEVEL_DEBUG` (10), but `toIntLevel(LogLevel.TRACE)` returns 
`LOG_LEVEL_TRACE` (0). The check `isLevelEnabled(TRACE)` evaluates `0 >= 10 = 
false`. TRACE logging is therefore never reachable via `setRootLoggerLevel()`.
   
   ```suggestion
                       case TRACE -> "trace";
                       case DEBUG -> "debug";
   ```



##########
impl/maven-core/src/main/java/org/apache/maven/logging/ProjectBuildLogAppender.java:
##########
@@ -109,29 +117,118 @@ public static void setForkingMojoId(String 
forkingMojoId) {
         }
     }
 
-    public static void updateMdc() {
-        String id = getProjectId();
-        if (id != null) {
-            MDC.put(KEY_PROJECT_ID, id);
-        } else {
-            MDC.remove(KEY_PROJECT_ID);
-        }
+    private static final AtomicLong SEQUENCE = new AtomicLong();
+
+    private static volatile Consumer<LogEvent> reportCapture;
+
+    public static void setReportCapture(Consumer<LogEvent> capture) {
+        ProjectBuildLogAppender.reportCapture = capture;
     }
 
     private final BuildEventListener buildEventListener;
+    private final ConcurrentLinkedQueue<LogEvent> queue = new 
ConcurrentLinkedQueue<>();
+    private final ReentrantLock drainLock = new ReentrantLock();
+    private boolean needsMetadata;

Review Comment:
   🟠 **[Not addressed] `needsMetadata` field is not `volatile` but is read from 
multiple threads.**
   
   `setNeedsMetadata()` (L143) writes this field; the inner 
`Sink.needsMetadata()` (L182) reads it from arbitrary logging threads. Without 
`volatile` (or another happens-before guarantee), the write may not be visible 
to the reader thread. This is the same concern raised in the prior review.
   
   ```suggestion
       private volatile boolean needsMetadata;
   ```



##########
api/maven-api-log/pom.xml:
##########
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven</groupId>
+    <artifactId>maven-api</artifactId>
+    <version>4.1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>maven-api-log</artifactId>
+  <name>Maven 4 API :: Logging</name>
+  <description>Maven 4 API - Unified Logging API (Log, LogBuilder, LogLevel, 
LogEvent, LogSink).</description>

Review Comment:
   🟠 **[Not addressed] Stale `<description>`: lists `Log, LogBuilder` — actual 
types are `Logger, LogEventBuilder`.**
   
   ```suggestion
     <description>Maven 4 API - Unified Logging API (Logger, LogEventBuilder, 
LogLevel, LogEvent, LogSink).</description>
   ```



##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLogEventBuilder.java:
##########
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.maven.api.logging.LogEventBuilder;
+import org.apache.maven.api.logging.LogLevel;
+
+/**
+ * Active {@link LogEventBuilder} returned by {@link DefaultLogger} when the 
target level is enabled.
+ * <p>
+ * Collects key-value pairs, throwable, and override context fields, then 
dispatches
+ * the event via {@link DefaultLogger#doLog} on the terminal {@code log()} 
call.
+ * Message formatting is deferred to the {@link DefaultLogEvent} — the raw 
template
+ * and arguments are passed through so that sinks can access structured data.
+ *
+ * @since 4.1.0
+ */
+final class DefaultLogEventBuilder implements LogEventBuilder {
+
+    private final DefaultLogger log;
+    private final LogLevel level;
+
+    private Map<String, String> kvMap;
+    private Throwable throwable;
+
+    DefaultLogEventBuilder(DefaultLogger log, LogLevel level) {
+        this.log = log;
+        this.level = level;
+    }
+
+    @Override
+    public LogEventBuilder withKeyValue(String key, String value) {
+        ensureKv();
+        kvMap.put(key, value);
+        return this;
+    }
+
+    @Override
+    public LogEventBuilder withKeyValue(String key, long value) {
+        return withKeyValue(key, Long.toString(value));
+    }
+
+    @Override
+    public LogEventBuilder withKeyValue(String key, boolean value) {
+        return withKeyValue(key, Boolean.toString(value));
+    }
+
+    @Override
+    public LogEventBuilder withThrowable(Throwable t) {
+        this.throwable = t;
+        return this;
+    }
+
+    @Override
+    public LogEventBuilder withProjectId(String projectId) {
+        return this;

Review Comment:
   🟡 **[Not addressed] `withProjectId()` is a silent no-op — contradicts API 
Javadoc.**
   
   The `LogEventBuilder` Javadoc says: *"Overrides the project identifier for 
this event."* The implementation discards the value. Callers using the fluent 
builder for async context hand-offs (the stated use case) will see wrong 
context on events.
   
   ```suggestion
       @Override
       public LogEventBuilder withProjectId(String projectId) {
           this.projectId = projectId;
           return this;
       }
   ```
   
   Add `private String projectId;` / `private String mojoId;` fields, and pass 
them to `doLog()`. If the intent is that context is always resolved from the 
`DefaultLogger` fields, document that explicitly instead.



##########
api/maven-api-log/src/main/java/org/apache/maven/api/logging/LogEventBuilder.java:
##########
@@ -0,0 +1,153 @@
+/*
+ * 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.api.logging;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Fluent builder for a single log statement.
+ * <p>
+ * Instances are obtained from {@link Log#atTrace()}, {@link Log#atDebug()}, 
etc.
+ * A builder <em>must</em> be consumed in a single chained call; it must not be
+ * stored in a variable across statements, as implementations may reuse
+ * thread-local state for zero-allocation dispatch.
+ * <p>
+ * When the target level is disabled the {@code Log} returns a shared
+ * {@code NopLogBuilder} singleton, making the entire fluent call chain a no-op

Review Comment:
   🟡 **[Not addressed] Stale Javadoc references.**
   
   - L27: `{@link Log#atTrace()}` — the type in this module is `Logger`, not 
`Log`. Should be `{@link Logger#atTrace()}`.
   - L32: `{@code Log}` — same, should be `{@code Logger}`.
   - L33: `NopLogBuilder` — the actual class is `NopLogEventBuilder`.



##########
api/maven-api-log/src/main/java/org/apache/maven/api/logging/LogSink.java:
##########
@@ -0,0 +1,61 @@
+/*
+ * 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.api.logging;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+
+/**
+ * Central dispatch point for log events.
+ * <p>
+ * All logging paths — the fluent {@link Log} API, SLF4J

Review Comment:
   🟡 **[Not addressed] Stale Javadoc: `{@link Log}` should be `{@link 
Logger}`.**
   
   ```suggestion
    * All logging paths — the fluent {@link Logger} API, SLF4J
   ```



##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLoggerManager.java:
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.logging.LogEvent;
+import org.apache.maven.api.logging.LogLevel;
+import org.apache.maven.api.logging.LogSink;
+import org.apache.maven.api.logging.Logger;
+import org.apache.maven.api.logging.LoggerManager;
+
+/**
+ * Default {@link LoggerManager} implementation. Owns:
+ * <ul>
+ *   <li>Per-logger level configuration (loaded from properties files)</li>
+ *   <li>The active {@link LogSink}</li>
+ *   <li>Severity tracking for {@code --fail-on-severity}</li>
+ * </ul>
+ * <p>
+ * This class has no SLF4J dependency. The SLF4J adapter layer creates
+ * an instance and registers it via {@link LoggerManager#set(LoggerManager)}.
+ */
+public class DefaultLoggerManager implements LoggerManager {
+
+    private static final String CONFIGURATION_FILE = "maven.logger.properties";
+
+    @Deprecated(since = "4.0.0")
+    private static final String LEGACY_CONFIGURATION_FILE = 
"simplelogger.properties";
+
+    private static final String LEGACY_PREFIX = "org.slf4j.simpleLogger.";
+
+    public static final int LOG_LEVEL_TRACE = 0;
+    public static final int LOG_LEVEL_DEBUG = 10;
+    public static final int LOG_LEVEL_INFO = 20;
+    public static final int LOG_LEVEL_WARN = 30;
+    public static final int LOG_LEVEL_ERROR = 40;
+    public static final int LOG_LEVEL_OFF = 50;
+
+    private volatile LogSink logSink;
+    private volatile int defaultLogLevel = LOG_LEVEL_INFO;
+    private final AtomicReference<LogLevel> maxReached = new 
AtomicReference<>(LogLevel.DEBUG);

Review Comment:
   🟠 **[Not addressed] `maxReached` initialized to `LogLevel.DEBUG` — 
`hasReached(DEBUG)` is `true` before anything is logged.**
   
   `LogLevel.DEBUG.ordinal()` is 1; `hasReached(LogLevel.TRACE)` returns `true` 
(1 ≥ 0) and `hasReached(LogLevel.DEBUG)` returns `true` (1 ≥ 1) immediately at 
construction, before any log event has been dispatched. `reconfigure()` also 
resets to `LogLevel.DEBUG`. The initial state should represent "nothing reached 
yet":
   
   ```suggestion
       private final AtomicReference<LogLevel> maxReached = new 
AtomicReference<>(null);
   ```
   
   And update `hasReached()`:
   ```java
   public boolean hasReached(LogLevel level) {
       LogLevel max = maxReached.get();
       return max != null && max.ordinal() >= level.ordinal();
   }
   ```
   
   And `reconfigure()`: `maxReached.set(null);`



##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLogEvent.java:
##########
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.time.Instant;
+import java.util.Map;
+
+import org.apache.maven.api.logging.LogEvent;
+import org.apache.maven.api.logging.LogLevel;
+
+/**
+ * Immutable implementation of {@link LogEvent} with lazy message formatting.
+ * <p>
+ * When constructed with a {@code messageTemplate} and {@code messageArgs},
+ * the formatted {@link #message()} is computed lazily on first access.
+ * When constructed with a pre-formatted message, it is returned as-is.
+ *
+ * @since 4.1.0
+ */
+public final class DefaultLogEvent implements LogEvent {
+
+    private static final String PLACEHOLDER = "{}";
+
+    private final Instant timestamp;
+    private final LogLevel level;
+    private final String message;
+    private final String loggerName;
+    private final String stackTrace;
+    private final Throwable throwable;
+    private final String sourceClassName;
+    private final String sourceMethodName;
+    private final long threadId;
+    private final long sequenceNumber;
+    private final String threadName;
+    private final String projectId;
+    private final String mojoId;
+    private final Map<String, String> keyValues;
+    private final String messageTemplate;
+    private final Object[] messageArgs;
+
+    private volatile String formattedMessage;
+
+    @SuppressWarnings("checkstyle:ParameterNumber")
+    public DefaultLogEvent(
+            Instant timestamp,
+            LogLevel level,
+            String message,
+            String loggerName,
+            String stackTrace,
+            Throwable throwable,
+            String sourceClassName,
+            String sourceMethodName,
+            long threadId,
+            long sequenceNumber,
+            String threadName,
+            String projectId,
+            String mojoId,
+            Map<String, String> keyValues,
+            String messageTemplate,
+            Object[] messageArgs) {
+        this.timestamp = timestamp;
+        this.level = level;
+        this.message = message;
+        this.loggerName = loggerName;
+        this.stackTrace = stackTrace;
+        this.throwable = throwable;
+        this.sourceClassName = sourceClassName;
+        this.sourceMethodName = sourceMethodName;
+        this.threadId = threadId;
+        this.sequenceNumber = sequenceNumber;
+        this.threadName = threadName;
+        this.projectId = projectId;
+        this.mojoId = mojoId;
+        this.keyValues = keyValues != null ? keyValues : Map.of();
+        this.messageTemplate = messageTemplate;
+        this.messageArgs = messageArgs;
+    }
+
+    /**
+     * Convenience constructor for events without structured data.
+     */
+    @SuppressWarnings("checkstyle:ParameterNumber")
+    public DefaultLogEvent(
+            Instant timestamp,
+            LogLevel level,
+            String message,
+            String loggerName,
+            String stackTrace,
+            Throwable throwable,
+            String sourceClassName,
+            String sourceMethodName,
+            long threadId,
+            long sequenceNumber,
+            String threadName,
+            String projectId,
+            String mojoId) {
+        this(
+                timestamp,
+                level,
+                message,
+                loggerName,
+                stackTrace,
+                throwable,
+                sourceClassName,
+                sourceMethodName,
+                threadId,
+                sequenceNumber,
+                threadName,
+                projectId,
+                mojoId,
+                Map.of(),
+                null,
+                null);
+    }
+
+    /**
+     * Convenience constructor for SLF4J events (no source metadata).
+     */
+    public DefaultLogEvent(Instant timestamp, LogLevel level, String message, 
String loggerName, String stackTrace) {
+        this(timestamp, level, message, loggerName, stackTrace, null, null, 
null, -1, -1, null, null, null);
+    }
+
+    @Override
+    public Instant timestamp() {
+        return timestamp;
+    }
+
+    @Override
+    public LogLevel level() {
+        return level;
+    }
+
+    @Override
+    public String message() {
+        if (message != null) {
+            return message;
+        }
+        String m = formattedMessage;
+        if (m == null) {
+            m = formatMessage(messageTemplate, messageArgs);
+            formattedMessage = m;
+        }
+        return m;
+    }
+
+    @Override
+    public String loggerName() {
+        return loggerName;
+    }
+
+    @Override
+    public String stackTrace() {
+        return stackTrace;
+    }
+
+    @Override
+    public Throwable throwable() {
+        return throwable;
+    }
+
+    @Override
+    public String sourceClassName() {
+        return sourceClassName;
+    }
+
+    @Override
+    public String sourceMethodName() {
+        return sourceMethodName;
+    }
+
+    @Override
+    public long threadId() {
+        return threadId;
+    }
+
+    @Override
+    public long sequenceNumber() {
+        return sequenceNumber;
+    }
+
+    @Override
+    public String threadName() {
+        return threadName;
+    }
+
+    @Override
+    public String projectId() {
+        return projectId;
+    }
+
+    @Override
+    public String mojoId() {
+        return mojoId;
+    }
+
+    @Override
+    public Map<String, String> keyValues() {
+        return keyValues;
+    }
+
+    @Override
+    public String messageTemplate() {
+        return messageTemplate;
+    }
+
+    @Override
+    public Object[] messageArgs() {
+        return messageArgs;

Review Comment:
   🟡 **[Not addressed] Mutable internal array returned from `@Immutable` type.**
   
   `LogEvent` is annotated `@Immutable`. `messageArgs()` returns the raw 
`Object[]` array. A sink could mutate the array between dispatch and the point 
lazy formatting in `message()` runs, corrupting the formatted output.
   
   ```suggestion
           return messageArgs != null ? messageArgs.clone() : null;
   ```



##########
impl/maven-logging/src/main/java/org/apache/maven/logging/impl/DefaultLogEventBuilder.java:
##########
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.logging.impl;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.maven.api.logging.LogEventBuilder;
+import org.apache.maven.api.logging.LogLevel;
+
+/**
+ * Active {@link LogEventBuilder} returned by {@link DefaultLogger} when the 
target level is enabled.
+ * <p>
+ * Collects key-value pairs, throwable, and override context fields, then 
dispatches
+ * the event via {@link DefaultLogger#doLog} on the terminal {@code log()} 
call.
+ * Message formatting is deferred to the {@link DefaultLogEvent} — the raw 
template
+ * and arguments are passed through so that sinks can access structured data.
+ *
+ * @since 4.1.0
+ */
+final class DefaultLogEventBuilder implements LogEventBuilder {
+
+    private final DefaultLogger log;
+    private final LogLevel level;
+
+    private Map<String, String> kvMap;
+    private Throwable throwable;
+
+    DefaultLogEventBuilder(DefaultLogger log, LogLevel level) {
+        this.log = log;
+        this.level = level;
+    }
+
+    @Override
+    public LogEventBuilder withKeyValue(String key, String value) {
+        ensureKv();
+        kvMap.put(key, value);
+        return this;
+    }
+
+    @Override
+    public LogEventBuilder withKeyValue(String key, long value) {
+        return withKeyValue(key, Long.toString(value));
+    }
+
+    @Override
+    public LogEventBuilder withKeyValue(String key, boolean value) {
+        return withKeyValue(key, Boolean.toString(value));
+    }
+
+    @Override
+    public LogEventBuilder withThrowable(Throwable t) {
+        this.throwable = t;
+        return this;
+    }
+
+    @Override
+    public LogEventBuilder withProjectId(String projectId) {
+        return this;
+    }
+
+    @Override
+    public LogEventBuilder withMojoId(String mojoId) {
+        return this;

Review Comment:
   🟡 **[Not addressed] `withMojoId()` is a silent no-op — same issue as 
`withProjectId()`.**



##########
api/maven-api-log/src/main/java/org/apache/maven/api/logging/Logger.java:
##########
@@ -0,0 +1,358 @@
+/*
+ * 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.api.logging;
+
+import java.util.function.Supplier;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
+import org.apache.maven.api.annotations.Provider;
+
+/**
+ * Unified logging interface for Maven plugins and CLI bootstrap code.
+ * <p>
+ * Replaces both {@code org.apache.maven.api.plugin.Log} (mojo logging) and
+ * {@code org.apache.maven.api.cli.Logger} (CLI bootstrap logging) with a 
single
+ * interface that supports a fluent builder API, structured key-value pairs, 
and
+ * lazy message evaluation.
+ * <p>
+ * Instances are obtained via dependency injection. A {@code @Singleton} bean
+ * may hold a long-lived {@code Logger} instance safely: context fields such as
+ * {@code projectId} and {@code mojoId} are resolved from a per-thread
+ * {@code LogContext} at event dispatch time, not at injection time.
+ *
+ * <h2>Fluent API (preferred)</h2>
+ * <pre>{@code
+ * log.atInfo()
+ *    .withKeyValue("artifact", artifact.getId())
+ *    .log("Resolved {} files", count);
+ * }</pre>
+ *
+ * <h2>Legacy convenience methods</h2>
+ * <pre>{@code
+ * log.info("Compiling {} source files", count);
+ * log.warn("Deprecated configuration key: {}", key, e);
+ * }</pre>
+ *
+ * @since 4.1.0
+ */
+@Experimental
+@Provider
+public interface Logger {
+
+    /**
+     * The name of this logger, typically the fully qualified class name of
+     * the component that owns it.
+     *
+     * @return the logger name, never {@code null}
+     */
+    @Nonnull
+    String getName();

Review Comment:
   🟡 **[Not addressed] Naming convention: `@Provider` types use noun-style 
accessors.**
   
   `Logger` is annotated `@Provider`. Maven 4 API convention for 
provider/immutable types uses `name()`, not `getName()`. Every new JDK 
immutable type since Java 8 uses noun-style; `getX()` is the JavaBeans 
mutable-bean prefix. Other methods on this interface (`isEnabled`, `atTrace`, 
`child`) follow the convention — `getName()` is the outlier.
   
   ```suggestion
       String name();
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to