This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 40fa637e06 TODO-365: rename commons Logger to RichLogger
40fa637e06 is described below

commit 40fa637e062b62b349f4dc5dbaeec14da44b7d1a
Author: James Bognar <[email protected]>
AuthorDate: Sat Aug 15 13:35:19 2026 -0400

    TODO-365: rename commons Logger to RichLogger
    
    Add pluggable MessageGenerator (printf default, MessageFormat, custom via 
builder), canonical-vs-view identity model with a weak-valued registry, unified 
log(LogRecord) capture interception with parent-chain listener propagation, 
capture ergonomics on LogRecordCapture, and fix the parent==null 
unregistered-logger dead-output bug. Sweep production + example consumers off 
the old commons Logger. Docs updated in juneau-docs. Also resolves Eclipse 
resource/deprecation warnings surfaced by [...]
---
 .../apache/juneau/bean/openapi3/ui/OpenApiUI.java  |   2 +-
 .../apache/juneau/bean/swagger/ui/SwaggerUI.java   |   2 +-
 .../juneau/commons/inject/BasicBeanStore.java      |   5 +-
 .../juneau/commons/inject/BeanInstantiator.java    |   2 +-
 .../apache/juneau/commons/logging/LogRecord.java   |  48 +++-
 .../juneau/commons/logging/LogRecordCapture.java   |  72 ++++-
 .../juneau/commons/logging/LogRecordListener.java  |   6 +-
 ...ogRecordListener.java => MessageGenerator.java} |  32 ++-
 .../logging/{Logger.java => RichLogger.java}       | 269 ++++++++++++++++--
 .../juneau/commons/logging/package-info.java       |   2 +-
 .../org/apache/juneau/commons/utils/IoUtils.java   |   2 +-
 .../apache/juneau/commons/utils/SystemUtils.java   |   2 +-
 .../juneau/commons/utils/ThrowableUtils.java       |   2 +-
 .../commons/inject/BeanInstantiator_Test.java      |  10 +-
 .../commons/logging/LogRecordCapture_Test.java     |  94 ++++++-
 .../juneau/commons/logging/LogRecord_Test.java     |  60 ++++
 .../{Logger_Test.java => RichLogger_Test.java}     | 312 +++++++++++++++++++--
 .../marshall/serializer/WriterSerializer.java      |   2 +-
 .../juneau/examples/bean/atom/AtomHtmlExample.java |   2 +-
 .../examples/core/html/HtmlComplexExample.java     |   2 +-
 .../examples/core/html/HtmlSimpleExample.java      |   4 +-
 .../examples/core/json/JsonComplexExample.java     |   2 +-
 .../core/json/JsonConfigurationExample.java        |   6 +-
 .../examples/core/json/JsonSimpleExample.java      |   4 +-
 .../juneau/examples/core/oapi/OapiExample.java     |   2 +-
 .../juneau/examples/core/svl/SvlExample.java       |  14 +-
 .../examples/core/uon/UonComplexExample.java       |   2 +-
 .../juneau/examples/core/uon/UonExample.java       |   2 +-
 .../examples/core/xml/XmlComplexExample.java       |   2 +-
 .../examples/core/xml/XmlConfigurationExample.java |  10 +-
 .../juneau/examples/core/xml/XmlSimpleExample.java |   2 +-
 .../org/apache/juneau/rest/server/RestContext.java |  20 +-
 .../apache/juneau/rest/server/RestResponse.java    |   1 -
 .../juneau/rest/server/servlet/RestResource.java   |   4 +-
 .../juneau/rest/server/servlet/RestServlet.java    |   4 +-
 .../swagger/BasicSwaggerProviderSession.java       |   2 +-
 .../server/util/CachingHttpServletRequest.java     |   3 +
 .../server/config/repository/GitControl.java       |   4 +-
 .../server/config/repository/GitControl_Test.java  |  29 +-
 39 files changed, 889 insertions(+), 156 deletions(-)

diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
index ce0435c710..3f32923abf 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
@@ -75,7 +75,7 @@ public class OpenApiUI extends ObjectSwap<OpenApi,Div> {
 
        private static final Set<String> STANDARD_METHODS = set("get", "put", 
"post", "delete", "options", "head", "patch", "trace");
 
-       private static final Logger LOG = Logger.getLogger(OpenApiUI.class);
+       private static final RichLogger LOG = 
RichLogger.getLogger(OpenApiUI.class);
 
        @SuppressWarnings({
                "null" // Null analysis not applicable for optional values
diff --git 
a/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
 
b/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
index ecb8ab50e7..f980135c5c 100644
--- 
a/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
+++ 
b/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
@@ -73,7 +73,7 @@ public class SwaggerUI extends ObjectSwap<Swagger,Div> {
 
        private static final Set<String> STANDARD_METHODS = set("get", "put", 
"post", "delete", "options");
 
-       private static final Logger LOG = Logger.getLogger(SwaggerUI.class);
+       private static final RichLogger LOG = 
RichLogger.getLogger(SwaggerUI.class);
 
        private static Div examples(Session s, ParameterInfo pi) {
                // @formatter:off
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
index 089ee8e882..eef842adef 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
@@ -25,9 +25,8 @@ import static org.apache.juneau.commons.utils.StringUtils.*;
 import java.util.*;
 import java.util.concurrent.*;
 import java.util.function.*;
-import java.util.logging.*;
-
 import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.commons.logging.*;
 import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.commons.settings.*;
 
@@ -69,7 +68,7 @@ import org.apache.juneau.commons.settings.*;
        "resource" // BeanStore is a fluent AutoCloseable; self-returns and 
owned/sentinel stores are not new resources to close.
 })
 public class BasicBeanStore implements WritableBeanStore {
-       private static final Logger LOGGER = 
Logger.getLogger(BasicBeanStore.class.getName());
+       private static final RichLogger LOGGER = 
RichLogger.getLogger(BasicBeanStore.class.getName());
 
        /**
         * Static reusable empty instance.
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
index 6da72f45f1..9ddfd7ae5c 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
@@ -214,7 +214,7 @@ public class BeanInstantiator<T> {
        /** Default builder class names for auto-detection. */
        protected static final Set<String> DEFAULT_BUILDER_CLASS_NAMES = 
u(set("Builder"));
 
-       private static final Logger logger = 
Logger.getLogger(BeanInstantiator.class);
+       private static final RichLogger logger = 
RichLogger.getLogger(BeanInstantiator.class);
 
        /**
         * Lifecycle scope for the bean being created.
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
index ff87597079..2424e0c0dd 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
@@ -37,14 +37,14 @@ import java.util.logging.*;
  *
  * <h5 class='section'>Usage:</h5>
  * <p>
- * This class is used internally by {@link Logger} when logging formatted 
messages.
+ * This class is used internally by {@link RichLogger} when logging formatted 
messages.
  * The formatted message is only computed when the LogRecord's message is 
actually
  * accessed (e.g., by a Handler or Formatter).
  * </p>
  *
  * <h5 class='section'>Formatting contract:</h5>
  * <p>
- * Juneau {@link Logger} patterns are <b>printf-style</b> (rendered here via 
{@link org.apache.juneau.commons.utils.StringUtils#format(String, Object...)}).
+ * Juneau {@link RichLogger} patterns are <b>printf-style</b> (rendered here 
via {@link org.apache.juneau.commons.utils.StringUtils#format(String, 
Object...)}).
  * Because this override returns the already-substituted message, downstream 
{@link java.util.logging.Handler Handlers} and
  * {@link java.util.logging.Formatter Formatters} that call {@link 
#getMessage()} see fully-rendered text and never re-apply
  * formatting — so the printf-vs-MessageFormat choice is purely internal. The 
JDK's {@link java.text.MessageFormat}
@@ -54,7 +54,7 @@ import java.util.logging.*;
  * </p>
  *
  * <h5 class='section'>See Also:</h5><ul>
- *     <li class='jc'>{@link Logger}
+ *     <li class='jc'>{@link RichLogger}
  *     <li class='jc'>{@link 
org.apache.juneau.commons.utils.StringUtils#format(String, Object...)}
  *     <li class='jc'>{@link java.util.logging.LogRecord}
  * </ul>
@@ -69,6 +69,7 @@ import java.util.logging.*;
 public class LogRecord extends java.util.logging.LogRecord {
 
        private static final long serialVersionUID = 1L;
+       private transient MessageGenerator generator;
 
        // Key constants for format placeholders
        private static final String KEY_date = "date";
@@ -91,10 +92,25 @@ public class LogRecord extends java.util.logging.LogRecord {
         * @param throwable The throwable, or <jk>null</jk> if none.
         */
        public LogRecord(String loggerName, Level level, String msg, Object[] 
parameters, Throwable throwable) {
+               this(loggerName, level, msg, parameters, throwable, 
MessageGenerator.PRINTF);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param loggerName The logger name.  Can be <jk>null</jk>.
+        * @param level The log level.  Must not be <jk>null</jk>.
+        * @param msg The message pattern (will be used as-is if args is null 
or empty).
+        * @param parameters The format arguments, or <jk>null</jk> if none.
+        * @param throwable The throwable, or <jk>null</jk> if none.
+        * @param generator The message generator.
+        */
+       public LogRecord(String loggerName, Level level, String msg, Object[] 
parameters, Throwable throwable, MessageGenerator generator) {
                super(level, msg);
                setLoggerName(loggerName);
                setParameters(isNotEmptyArray(parameters) ? parameters : null);
                setThrown(throwable);
+               this.generator = generator == null ? MessageGenerator.PRINTF : 
generator;
        }
 
        /**
@@ -110,7 +126,10 @@ public class LogRecord extends java.util.logging.LogRecord 
{
        public String getMessage() {
                var m = super.getMessage();
                var p = getParameters();
-               return isEmptyArray(p) ? m : f(m, p);
+               if (isEmptyArray(p))
+                       return m;
+               var messageGenerator = generator == null ? 
MessageGenerator.PRINTF : generator;
+               return safeOptCatch(() -> messageGenerator.format(m, p), x -> 
m).orElse(m);
        }
 
        /**
@@ -202,11 +221,22 @@ public class LogRecord extends 
java.util.logging.LogRecord {
         * @see SimpleFormatter
         * @see Formatter
         */
+       public String formatted(String format) {
+               return formatted(this, format);
+       }
+
+       /**
+        * Formats a JUL log record with the same placeholders as {@link 
#formatted(String)}.
+        *
+        * @param record The record to format.
+        * @param format The format string.
+        * @return The formatted string.
+        */
        @SuppressWarnings({
                "deprecation" // Date constructor is deprecated but needed for 
compatibility
        })
-       public String formatted(String format) {
-               var date = new Date(getMillis());
+       public static String formatted(java.util.logging.LogRecord record, 
String format) {
+               var date = new Date(record.getMillis());
 
                Function<String,Object> resolver = key -> switch (key) {
                        case KEY_date -> "%1$s";
@@ -215,11 +245,11 @@ public class LogRecord extends 
java.util.logging.LogRecord {
                        case KEY_msg -> "%4$s";
                        case KEY_thrown -> "%5$s";
                        case KEY_timestamp -> new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(date);
-                       case KEY_thread, KEY_threadid -> s(getThreadID());
-                       case KEY_exception -> o(getThrown()).map(x -> 
x.getMessage()).orElse("");
+                       case KEY_thread, KEY_threadid -> 
s(record.getThreadID());
+                       case KEY_exception -> o(record.getThrown()).map(x -> 
x.getMessage()).orElse("");
                        default -> "";
                };
 
-               return safeOptCatch(()->f(formatNamed(format, resolver), date, 
getLoggerName(), getLevel(), getMessage(), getThrown()), x -> 
x.getLocalizedMessage()).orElse(null);
+               return safeOptCatch(() -> f(formatNamed(format, resolver), 
date, record.getLoggerName(), record.getLevel(), record.getMessage(), 
record.getThrown()), x -> x.getLocalizedMessage()).orElse(null);
        }
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
index 9829527e5d..932d0fee94 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
@@ -18,6 +18,8 @@ package org.apache.juneau.commons.logging;
 
 import java.io.*;
 import java.util.*;
+import java.util.function.*;
+import java.util.regex.*;
 
 /**
  * Captures log records for testing purposes.
@@ -30,7 +32,7 @@ import java.util.*;
  * <h5 class='section'>Usage:</h5>
  * <p class='bjava'>
  *     <jc>// Capture records using try-with-resources</jc>
- *     <jk>try</jk> (LogRecordCapture <jv>capture</jv> = 
Logger.getLogger(MyClass.<jk>class</jk>).captureEvents()) {
+ *     <jk>try</jk> (LogRecordCapture <jv>capture</jv> = 
RichLogger.getLogger(MyClass.<jk>class</jk>).captureEvents()) {
  *             <jv>logger</jv>.info(<js>"Test message"</js>);
  *             <jv>logger</jv>.warning(<js>"Warning message"</js>);
  *
@@ -50,22 +52,31 @@ import java.util.*;
  * </p>
  *
  * <h5 class='section'>See Also:</h5><ul>
- *     <li class='jc'>{@link Logger#captureEvents()}
+ *     <li class='jc'>{@link RichLogger#captureEvents()}
  *     <li class='jc'>{@link LogRecord#formatted(String)}
  * </ul>
  */
 public class LogRecordCapture implements LogRecordListener, Closeable {
 
-       private final Logger logger;
-       private final List<LogRecord> records = 
Collections.synchronizedList(new ArrayList<>());
+       private final RichLogger logger;
+       private final List<RichLogger> pinnedLoggers = new ArrayList<>();
+       private final List<java.util.logging.LogRecord> records = 
Collections.synchronizedList(new ArrayList<>());
+       private final Predicate<java.util.logging.LogRecord> filter;
 
        /**
         * Constructor.
         *
         * @param logger The logger to capture records from.
         */
-       LogRecordCapture(Logger logger) {
+       LogRecordCapture(RichLogger logger) {
+               this(logger, x -> true);
+       }
+
+       LogRecordCapture(RichLogger logger, 
Predicate<java.util.logging.LogRecord> filter) {
                this.logger = logger;
+               this.filter = filter == null ? x -> true : filter;
+               pinnedLoggers.add(logger);
+               RichLogger.forEachLiveAncestor(logger.getName(), 
pinnedLoggers::add);
                logger.addLogRecordListener(this);
        }
 
@@ -75,8 +86,9 @@ public class LogRecordCapture implements LogRecordListener, 
Closeable {
         * @param rec The log record that was logged.
         */
        @Override
-       public void onLogRecord(LogRecord rec) {
-               records.add(rec);
+       public void onLogRecord(java.util.logging.LogRecord rec) {
+               if (filter.test(rec))
+                       records.add(rec);
        }
 
        /**
@@ -84,7 +96,7 @@ public class LogRecordCapture implements LogRecordListener, 
Closeable {
         *
         * @return An unmodifiable list of captured LogRecords.
         */
-       public List<LogRecord> getRecords() {
+       public List<java.util.logging.LogRecord> getRecords() {
                synchronized (records) {
                        return List.copyOf(records);
                }
@@ -102,8 +114,7 @@ public class LogRecordCapture implements LogRecordListener, 
Closeable {
        public List<String> getRecords(String format) {
                synchronized (records) {
                        return records.stream()
-                               .map(LogRecord.class::cast)
-                               .map(x -> x.formatted(format))
+                               .map(x -> LogRecord.formatted(x, format))
                                .toList();
                }
        }
@@ -133,11 +144,52 @@ public class LogRecordCapture implements 
LogRecordListener, Closeable {
                return records.isEmpty();
        }
 
+       public List<String> messages() {
+               synchronized (records) {
+                       return 
records.stream().map(java.util.logging.LogRecord::getMessage).toList();
+               }
+       }
+
+       public java.util.logging.LogRecord last() {
+               synchronized (records) {
+                       return records.isEmpty() ? null : 
records.get(records.size() - 1);
+               }
+       }
+
+       public List<java.util.logging.LogRecord> 
byLevel(java.util.logging.Level level) {
+               synchronized (records) {
+                       return records.stream().filter(x -> 
x.getLevel().equals(level)).toList();
+               }
+       }
+
+       public List<java.util.logging.LogRecord> matching(String regex) {
+               var p = Pattern.compile(regex);
+               synchronized (records) {
+                       return records.stream().filter(x -> 
p.matcher(x.getMessage()).find()).toList();
+               }
+       }
+
+       public String assertMessage() {
+               var r = last();
+               return r == null ? null : r.getMessage();
+       }
+
+       public java.util.logging.Level assertLevel() {
+               var r = last();
+               return r == null ? null : r.getLevel();
+       }
+
+       public Throwable assertThrown() {
+               var r = last();
+               return r == null ? null : r.getThrown();
+       }
+
        /**
         * Closes this capture and removes it from the logger's listeners.
         */
        @Override
        public void close() {
                logger.removeLogRecordListener(this);
+               pinnedLoggers.clear();
        }
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
index d70ea1e133..981606ff78 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
@@ -20,12 +20,12 @@ package org.apache.juneau.commons.logging;
  * Interface for listening to log records.
  *
  * <p>
- * Implementations of this interface can be registered with a {@link Logger} 
to receive
+ * Implementations of this interface can be registered with a {@link 
RichLogger} to receive
  * notifications when log records are logged.
  *
  * <h5 class='section'>See Also:</h5><ul>
  *     <li class='jc'>{@link LogRecordCapture}
- *     <li class='jc'>{@link Logger}
+ *     <li class='jc'>{@link RichLogger}
  * </ul>
  */
 public interface LogRecordListener {
@@ -35,5 +35,5 @@ public interface LogRecordListener {
         *
         * @param rec The log record that was logged.
         */
-       void onLogRecord(LogRecord rec);
+       void onLogRecord(java.util.logging.LogRecord rec);
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/MessageGenerator.java
similarity index 52%
copy from 
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
copy to 
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/MessageGenerator.java
index d70ea1e133..7003e50414 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/MessageGenerator.java
@@ -17,23 +17,27 @@
 package org.apache.juneau.commons.logging;
 
 /**
- * Interface for listening to log records.
- *
- * <p>
- * Implementations of this interface can be registered with a {@link Logger} 
to receive
- * notifications when log records are logged.
- *
- * <h5 class='section'>See Also:</h5><ul>
- *     <li class='jc'>{@link LogRecordCapture}
- *     <li class='jc'>{@link Logger}
- * </ul>
+ * Strategy interface for rendering log message patterns.
  */
-public interface LogRecordListener {
+@FunctionalInterface
+public interface MessageGenerator {
+
+       /**
+        * Printf-style message generator backed by {@link #format(String, 
Object...)}.
+        */
+       MessageGenerator PRINTF = (pattern, args) -> 
org.apache.juneau.commons.utils.StringUtils.format(pattern, args);
+
+       /**
+        * MessageFormat-style message generator backed by {@link 
org.apache.juneau.commons.utils.StringUtils#mformat(String, Object...)}.
+        */
+       MessageGenerator MESSAGE_FORMAT = (pattern, args) -> 
org.apache.juneau.commons.utils.StringUtils.mformat(pattern, args);
 
        /**
-        * Called when a log record is logged.
+        * Renders a message pattern with arguments.
         *
-        * @param rec The log record that was logged.
+        * @param pattern The message pattern.
+        * @param args The message arguments.
+        * @return The rendered message.
         */
-       void onLogRecord(LogRecord rec);
+       String format(String pattern, Object...args);
 }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/Logger.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/RichLogger.java
similarity index 63%
rename from 
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/Logger.java
rename to 
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/RichLogger.java
index 1675efa936..6b4209d8e0 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/Logger.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/RichLogger.java
@@ -19,8 +19,10 @@ package org.apache.juneau.commons.logging;
 import static java.util.logging.Level.*;
 import static org.apache.juneau.commons.utils.Shorts.*;
 
+import java.lang.ref.*;
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.function.*;
 import java.util.logging.*;
 
 /**
@@ -31,7 +33,7 @@ import java.util.logging.*;
  * an underlying {@link java.util.logging.Logger} instance. This design 
provides:
  * </p>
  * <ul>
- *     <li>Type compatibility - extends Logger for use anywhere a Logger is 
expected
+ *     <li>Type compatibility - extends RichLogger for use anywhere a 
RichLogger is expected
  *     <li>Flexibility - can wrap existing logger instances
  *     <li>Convenience methods for formatted logging using {@link 
org.apache.juneau.commons.utils.Shorts#fs(String, Object...)}
  * </ul>
@@ -55,7 +57,7 @@ import java.util.logging.*;
  * </ul>
  *
  * <p>
- * Juneau {@link Logger} patterns are printf-style. Because {@link 
LogRecord#getMessage()} returns the
+ * Juneau {@link RichLogger} patterns are printf-style. Because {@link 
LogRecord#getMessage()} returns the
  * already-substituted message, the {@link java.text.MessageFormat} parameter 
contract of the underlying
  * {@link java.util.logging.LogRecord} only surfaces if a record is serialized 
and consumed as a plain
  * {@link java.util.logging.LogRecord} by a non-Juneau handler.
@@ -63,7 +65,7 @@ import java.util.logging.*;
  *
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
- *     Logger <jv>logger</jv> = 
Logger.getLogger(MyClass.<jk>class</jk>.getName());
+ *     RichLogger <jv>logger</jv> = 
RichLogger.getLogger(MyClass.<jk>class</jk>.getName());
  *
  *     <jc>// Formatted logging at different levels (printf-style 
placeholders)</jc>
  *     <jv>logger</jv>.severe(<js>"Error processing user %s: %s"</js>, userId, 
error);
@@ -83,56 +85,91 @@ import java.util.logging.*;
        "java:S1192", // String literals intentionally duplicated for clarity
        "java:S2176"  // Class name intentionally matches 
java.util.logging.Logger; extends it to provide Juneau-specific logging 
enhancements
 })
-public class Logger extends java.util.logging.Logger {
+public class RichLogger extends java.util.logging.Logger {
 
        /**
         * Permanent registry of logger instances by name.
         *
         * <p>
         * Uses an unbounded {@link ConcurrentHashMap} so every name maps to 
exactly one
-        * {@link Logger} instance for the JVM lifetime.  An evictable cache 
(e.g. one with a
+        * {@link RichLogger} instance for the JVM lifetime.  An evictable 
cache (e.g. one with a
         * {@code maxSize} that clears the whole map) would cause a class-loaded
-        * {@code static final Logger} field to refer to an instance that is no 
longer in the
+        * {@code static final RichLogger} field to refer to an instance that 
is no longer in the
         * registry; a subsequent call to {@link #getLogger(String)} would then 
return a new,
         * different instance, breaking {@link LogRecordCapture} and any other 
code that
         * assumes one name == one identity.
         */
-       private static final ConcurrentHashMap<String,Logger> loggers = new 
ConcurrentHashMap<>();
+       private static final ConcurrentHashMap<String,LoggerRef> loggers = new 
ConcurrentHashMap<>();
+       private static final ReferenceQueue<RichLogger> loggerQueue = new 
ReferenceQueue<>();
+
+       private static final class LoggerRef extends WeakReference<RichLogger> {
+               final String name;
+
+               LoggerRef(String name, RichLogger referent, 
ReferenceQueue<RichLogger> q) {
+                       super(referent, q);
+                       this.name = name;
+               }
+       }
 
        /**
         * The underlying logger instance that we delegate to.
         */
        private final java.util.logging.Logger delegate;
 
+       private final MessageGenerator generator;
+       private final RichLogger canonical;
+
        /**
         * List of log record listeners.
         */
-       private final List<LogRecordListener> listeners = 
Collections.synchronizedList(new ArrayList<>());
+       private final List<LogRecordListener> listeners;
+       private volatile boolean useParentListeners = true;
 
        /**
         * Protected constructor - wraps an existing logger instance.
         *
         * @param delegate The underlying logger to delegate to.  Must not be 
<jk>null</jk>.
         */
-       protected Logger(java.util.logging.Logger delegate) {
+       protected RichLogger(java.util.logging.Logger delegate) {
+               this(delegate, MessageGenerator.PRINTF, null);
+       }
+
+       private RichLogger(java.util.logging.Logger delegate, MessageGenerator 
generator, RichLogger canonical) {
                super(delegate.getName(), null);
                this.delegate = delegate;
+               this.generator = generator == null ? MessageGenerator.PRINTF : 
generator;
+               this.canonical = canonical == null ? this : canonical;
+               this.listeners = canonical == null ? 
Collections.synchronizedList(new ArrayList<>()) : null;
        }
 
        /**
         * Creates a logger for the specified name.
         *
         * <p>
-        * This method returns the same Logger instance for a given name, 
ensuring that
+        * This method returns the same RichLogger instance for a given name, 
ensuring that
         * listeners attached to a logger persist across multiple calls to 
{@link #getLogger(String)}.
-        * Logger instances are cached and automatically created using the 
underlying
+        * RichLogger instances are cached and automatically created using the 
underlying
         * {@link java.util.logging.Logger#getLogger(String)}.
         *
         * @param name The logger name.  Must not be <jk>null</jk>.
         * @return A logger instance (cached and reused for the same name).
         */
-       public static Logger getLogger(String name) {
-               return loggers.computeIfAbsent(name, k -> new 
Logger(java.util.logging.Logger.getLogger(k)));
+       public static RichLogger getLogger(String name) {
+               drainCollectedLoggers();
+               while (true) {
+                       var currentRef = loggers.get(name);
+                       var current = currentRef == null ? null : 
currentRef.get();
+                       if (current != null)
+                               return current;
+                       var created = new 
RichLogger(java.util.logging.Logger.getLogger(name));
+                       var createdRef = new LoggerRef(name, created, 
loggerQueue);
+                       if (currentRef == null) {
+                               if (loggers.putIfAbsent(name, createdRef) == 
null)
+                                       return created;
+                       } else if (loggers.replace(name, currentRef, 
createdRef))
+                               return created;
+                       drainCollectedLoggers();
+               }
        }
 
        /**
@@ -141,10 +178,96 @@ public class Logger extends java.util.logging.Logger {
         * @param clazz The class.  Must not be <jk>null</jk>.
         * @return A logger instance.
         */
-       public static Logger getLogger(Class<?> clazz) {
+       public static RichLogger getLogger(Class<?> clazz) {
                return getLogger(cn(clazz));
        }
 
+       static RichLogger findLogger(String name) {
+               drainCollectedLoggers();
+               var ref = loggers.get(name);
+               if (ref == null)
+                       return null;
+               var logger = ref.get();
+               if (logger == null)
+                       loggers.remove(name, ref);
+               return logger;
+       }
+
+       static void forEachLiveAncestor(String name, Consumer<RichLogger> 
action) {
+               var ancestorName = parentName(name);
+               while (ancestorName != null) {
+                       var ancestor = findLogger(ancestorName);
+                       if (ancestor != null)
+                               action.accept(ancestor);
+                       ancestorName = parentName(ancestorName);
+               }
+       }
+
+       private static String parentName(String name) {
+               if (name == null || name.isEmpty())
+                       return null;
+               var i = name.lastIndexOf('.');
+               return i == -1 ? "" : name.substring(0, i);
+       }
+
+       private static void drainCollectedLoggers() {
+               LoggerRef ref;
+               while ((ref = (LoggerRef)loggerQueue.poll()) != null)
+                       loggers.remove(ref.name, ref);
+       }
+
+       /**
+        * Creates a builder for the specified logger name.
+        *
+        * @param name The logger name.
+        * @return A new builder.
+        */
+       public static Builder builder(String name) {
+               return new Builder(name);
+       }
+
+       /**
+        * Creates a builder for the specified class logger name.
+        *
+        * @param clazz The class.
+        * @return A new builder.
+        */
+       public static Builder builder(Class<?> clazz) {
+               return builder(cn(clazz));
+       }
+
+       /**
+        * Builder for creating configured {@link RichLogger} views.
+        */
+       public static class Builder {
+               private final String name;
+               private MessageGenerator generator = MessageGenerator.PRINTF;
+
+               Builder(String name) {
+                       this.name = name;
+               }
+
+               public Builder printf() {
+                       generator = MessageGenerator.PRINTF;
+                       return this;
+               }
+
+               public Builder messageFormat() {
+                       generator = MessageGenerator.MESSAGE_FORMAT;
+                       return this;
+               }
+
+               public Builder generator(MessageGenerator value) {
+                       generator = value == null ? MessageGenerator.PRINTF : 
value;
+                       return this;
+               }
+
+               public RichLogger build() {
+                       var canonical = getLogger(name);
+                       return new RichLogger(canonical.delegate, generator, 
canonical);
+               }
+       }
+
        /**
         * Creates a log record capture for testing.
         *
@@ -158,7 +281,21 @@ public class Logger extends java.util.logging.Logger {
                "resource" // Caller takes ownership of the returned 
LogRecordCapture
        })
        public LogRecordCapture captureEvents() {
-               return new LogRecordCapture(this);
+               return new LogRecordCapture(canonical);
+       }
+
+       @SuppressWarnings({
+               "resource" // Caller takes ownership of the returned 
LogRecordCapture
+       })
+       public LogRecordCapture captureEvents(Level min) {
+               return new LogRecordCapture(canonical, x -> 
x.getLevel().intValue() >= min.intValue());
+       }
+
+       @SuppressWarnings({
+               "resource" // Caller takes ownership of the returned 
LogRecordCapture
+       })
+       public LogRecordCapture 
captureEvents(Predicate<java.util.logging.LogRecord> filter) {
+               return new LogRecordCapture(canonical, filter);
        }
 
        /**
@@ -167,7 +304,7 @@ public class Logger extends java.util.logging.Logger {
         * @param listener The listener to add.
         */
        void addLogRecordListener(LogRecordListener listener) {
-               listeners.add(listener);
+               canonical.listeners.add(listener);
        }
 
        /**
@@ -176,7 +313,46 @@ public class Logger extends java.util.logging.Logger {
         * @param listener The listener to remove.
         */
        void removeLogRecordListener(LogRecordListener listener) {
-               listeners.remove(listener);
+               canonical.listeners.remove(listener);
+       }
+
+       public void setUseParentListeners(boolean useParentListeners) {
+               canonical.useParentListeners = useParentListeners;
+       }
+
+       public boolean isUseParentListeners() {
+               return canonical.useParentListeners;
+       }
+
+       @Override
+       public void log(java.util.logging.LogRecord record) {
+               canonical.listeners.forEach(x -> x.onLogRecord(record));
+               if (canonical.useParentListeners) {
+                       var ancestors = new ArrayList<RichLogger>();
+                       forEachLiveAncestor(canonical.getName(), 
ancestors::add);
+                       for (var ancestor : ancestors) {
+                               ancestor.listeners.forEach(x -> 
x.onLogRecord(record));
+                               if (!ancestor.useParentListeners)
+                                       break;
+                       }
+               }
+               delegate.log(record);
+       }
+
+       private static boolean hasAnyListenerInChain(RichLogger logger) {
+               if (!logger.listeners.isEmpty())
+                       return true;
+               if (!logger.useParentListeners)
+                       return false;
+               var ancestors = new ArrayList<RichLogger>();
+               forEachLiveAncestor(logger.getName(), ancestors::add);
+               for (var ancestor : ancestors) {
+                       if (!ancestor.listeners.isEmpty())
+                               return true;
+                       if (!ancestor.useParentListeners)
+                               break;
+               }
+               return false;
        }
 
        /**
@@ -196,62 +372,105 @@ public class Logger extends java.util.logging.Logger {
         * @param thrown The throwable, or <jk>null</jk> if none.
         */
        private void doLog(Level level, String msg, Object[] args, Throwable 
thrown) {
-               if (!isLoggable(level) && listeners.isEmpty())
+               if (!isLoggable(level) && !hasAnyListenerInChain(canonical))
                        return;
 
                // Create LogRecord with lazy formatting support
-               var rec = new LogRecord(getName(), level, msg, args, thrown);
-
-               // Notify all listeners
-               listeners.forEach(x -> x.onLogRecord(rec));
+               var rec = new LogRecord(getName(), level, msg, args, thrown, 
generator);
+               this.log(rec);
+       }
 
-               // Delegate to underlying logger (LogRecord extends 
java.util.logging.LogRecord)
-               delegate.log(rec);
+       private void logSupplier(Level level, Supplier<String> msgSupplier, 
Throwable thrown) {
+               if (!isLoggable(level) && !hasAnyListenerInChain(canonical))
+                       return;
+               var msg = msgSupplier == null ? null : msgSupplier.get();
+               var rec = new LogRecord(getName(), level, msg, null, thrown, 
generator);
+               this.log(rec);
        }
 
        // Convenience methods with formatted strings
 
-       // Standard Logger methods - feed through central doLog method
+       // Standard RichLogger methods - feed through central doLog method
        @Override
        public void severe(String msg) {
                doLog(SEVERE, msg, null, null);
        }
 
+       @Override
+       public void severe(Supplier<String> msgSupplier) {
+               logSupplier(SEVERE, msgSupplier, null);
+       }
+
        @Override
        public void warning(String msg) {
                doLog(WARNING, msg, null, null);
        }
 
+       @Override
+       public void warning(Supplier<String> msgSupplier) {
+               logSupplier(WARNING, msgSupplier, null);
+       }
+
        @Override
        public void info(String msg) {
                doLog(INFO, msg, null, null);
        }
 
+       @Override
+       public void info(Supplier<String> msgSupplier) {
+               logSupplier(INFO, msgSupplier, null);
+       }
+
        @Override
        public void config(String msg) {
                doLog(CONFIG, msg, null, null);
        }
 
+       @Override
+       public void config(Supplier<String> msgSupplier) {
+               logSupplier(CONFIG, msgSupplier, null);
+       }
+
        @Override
        public void fine(String msg) {
                doLog(FINE, msg, null, null);
        }
 
+       @Override
+       public void fine(Supplier<String> msgSupplier) {
+               logSupplier(FINE, msgSupplier, null);
+       }
+
        @Override
        public void finer(String msg) {
                doLog(FINER, msg, null, null);
        }
 
+       @Override
+       public void finer(Supplier<String> msgSupplier) {
+               logSupplier(FINER, msgSupplier, null);
+       }
+
        @Override
        public void finest(String msg) {
                doLog(FINEST, msg, null, null);
        }
 
+       @Override
+       public void finest(Supplier<String> msgSupplier) {
+               logSupplier(FINEST, msgSupplier, null);
+       }
+
        @Override
        public void log(Level level, String msg) {
                doLog(level, msg, null, null);
        }
 
+       @Override
+       public void log(Level level, Supplier<String> msgSupplier) {
+               logSupplier(level, msgSupplier, null);
+       }
+
        @Override
        public void log(Level level, String msg, Object param1) {
                doLog(level, msg, new Object[]{param1}, null);
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
index 190f0828d5..dece8ce766 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
@@ -19,7 +19,7 @@
  * Lightweight logging utilities built on top of {@code java.util.logging}.
  *
  * <p>
- * Provides an extended {@link org.apache.juneau.commons.logging.Logger} with 
formatted-message
+ * Provides an extended {@link org.apache.juneau.commons.logging.RichLogger} 
with formatted-message
  * convenience methods, a lazy-formatting {@link 
org.apache.juneau.commons.logging.LogRecord}, and
  * facilities for {@link org.apache.juneau.commons.logging.LogRecordCapture 
capturing} and
  * {@link org.apache.juneau.commons.logging.LogRecordListener listening to} 
log records.
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
index 3a09490e7c..ff4cf5a127 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
@@ -1031,7 +1031,7 @@ public class IoUtils {
         * @param lines The lines to print.  Must not be <jk>null</jk> 
(individual <jk>null</jk> entries render as <js>"null"</js>).
         */
        public static final void printLines(String[] lines) {
-               Logger log = Logger.getLogger(IoUtils.class);
+               RichLogger log = RichLogger.getLogger(IoUtils.class);
                for (var i = 0; i < lines.length; i++)
                        log.info(String.format("%4s:%s", i + 1, lines[i]));
        }
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
index f1e9d9fc83..0269d13224 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
@@ -30,7 +30,7 @@ import org.apache.juneau.commons.settings.*;
  */
 public class SystemUtils {
 
-       private static final Logger LOG = Logger.getLogger(SystemUtils.class);
+       private static final RichLogger LOG = 
RichLogger.getLogger(SystemUtils.class);
 
        /**
         * Prevents instantiation.
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
index 1b89279d4d..9e31eac292 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
@@ -58,7 +58,7 @@ import org.apache.juneau.commons.settings.*;
  */
 public class ThrowableUtils {
 
-       private static final Logger LOG = 
Logger.getLogger(ThrowableUtils.class);
+       private static final RichLogger LOG = 
RichLogger.getLogger(ThrowableUtils.class);
 
        static final Setting<Boolean> VERBOSE = 
Settings.get().get("juneau.enableVerboseExceptions").asBoolean();
 
diff --git 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
index b8369fd248..6ad9fd402c 100644
--- 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
+++ 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
@@ -26,7 +26,7 @@ import java.util.logging.*;
 
 import org.apache.juneau.commons.*;
 import org.apache.juneau.commons.lang.*;
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
 import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.commons.utils.*;
 import org.junit.jupiter.api.*;
@@ -3842,7 +3842,7 @@ class BeanInstantiator_Test extends TestBase {
                void q01_loggingOnSimpleBeanCreation() {
                        // Get the logger instance using the same method as 
BeanInstantiator static field
                        // This ensures we get the same cached instance
-                       var logger = Logger.getLogger(BeanInstantiator.class);
+                       var logger = 
RichLogger.getLogger(BeanInstantiator.class);
                        logger.setLevel(Level.FINE); // Enable FINE level 
logging
 
                        try (var capture = logger.captureEvents()) {
@@ -3871,7 +3871,7 @@ class BeanInstantiator_Test extends TestBase {
                 */
                @Test
                void q02_loggingOnBuilderBeanCreation() {
-                       var logger = Logger.getLogger(BeanInstantiator.class);
+                       var logger = 
RichLogger.getLogger(BeanInstantiator.class);
                        logger.setLevel(Level.FINE); // Enable FINE level 
logging
                        try (var capture = logger.captureEvents()) {
                                var bean = bc(Q02_BeanWithBuilder.class).run();
@@ -3893,7 +3893,7 @@ class BeanInstantiator_Test extends TestBase {
                 */
                @Test
                void q03_logMessagesIncludeBeanTypePrefix() {
-                       var logger = Logger.getLogger(BeanInstantiator.class);
+                       var logger = 
RichLogger.getLogger(BeanInstantiator.class);
                        logger.setLevel(Level.FINE); // Enable FINE level 
logging
                        try (var capture = logger.captureEvents()) {
                                var bean = bc(SimpleBean.class).run();
@@ -3918,7 +3918,7 @@ class BeanInstantiator_Test extends TestBase {
                 */
                @Test
                void q04_logMessagesWithFormatArguments() {
-                       var logger = Logger.getLogger(BeanInstantiator.class);
+                       var logger = 
RichLogger.getLogger(BeanInstantiator.class);
                        try (var capture = logger.captureEvents()) {
                                // Create a bean that will trigger logging with 
format arguments
                                var bean = bc(Q02_BeanWithBuilder.class).run();
diff --git 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
index 2efef2beff..f1a62bc1e5 100644
--- 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
+++ 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
@@ -19,6 +19,7 @@ package org.apache.juneau.commons.logging;
 import static org.apache.juneau.commons.TestAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
+import java.util.*;
 import java.util.logging.*;
 
 import org.apache.juneau.commons.*;
@@ -29,8 +30,8 @@ import org.junit.jupiter.api.*;
  */
 class LogRecordCapture_Test extends TestBase {
 
-       private static Logger getLogger(String name) {
-               var l = Logger.getLogger(name);
+       private static RichLogger getLogger(String name) {
+               var l = RichLogger.getLogger(name);
                l.setLevel(Level.OFF);
                return l;
        }
@@ -273,7 +274,7 @@ class LogRecordCapture_Test extends TestBase {
        }
 
        
//====================================================================================================
-       // Integration with Logger
+       // Integration with RichLogger
        
//====================================================================================================
 
        @Test void h01_capturesAllLevels() {
@@ -323,4 +324,91 @@ class LogRecordCapture_Test extends TestBase {
                        assertSame(exception, records.get(0).getThrown());
                }
        }
+
+       @Test void i01_capture_acceptsPlainJulRecords() {
+               var logger = RichLogger.getLogger("i01");
+               try (var capture = logger.captureEvents()) {
+                       var rec = new java.util.logging.LogRecord(Level.INFO, 
"plain");
+                       rec.setLoggerName("i01");
+                       logger.log(rec);
+
+                       var records = capture.getRecords();
+                       assertSize(1, records);
+                       assertEquals("plain", records.get(0).getMessage());
+                       assertFalse(records.get(0) instanceof LogRecord);
+               }
+       }
+
+       @Test void i02_getRecordsFormatted_handlesPlainJulRecords() {
+               var logger = RichLogger.getLogger("i02");
+               try (var capture = logger.captureEvents()) {
+                       var rec = new 
java.util.logging.LogRecord(Level.WARNING, "plain-format");
+                       rec.setLoggerName("i02");
+                       logger.log(rec);
+
+                       var formatted = capture.getRecords("{level}:{msg}");
+                       assertSize(1, formatted);
+                       assertEquals("WARNING:plain-format", formatted.get(0));
+               }
+       }
+
+       @Test void j01_captureEvents_levelThreshold_usesJulDirection() {
+               var logger = RichLogger.getLogger("j01");
+               try (var capture = logger.captureEvents(Level.FINE)) {
+                       logger.finer("finer");
+                       logger.fine("fine");
+                       logger.info("info");
+                       logger.severe("severe");
+
+                       var messages = capture.messages();
+                       assertEquals(List.of("fine", "info", "severe"), 
messages);
+               }
+       }
+
+       @Test void j02_captureEvents_predicate_filtersAtCaptureTime() {
+               var logger = RichLogger.getLogger("j02");
+               try (var capture = logger.captureEvents(x -> 
x.getMessage().contains("keep"))) {
+                       logger.info("keep-1");
+                       logger.info("drop");
+                       logger.info("keep-2");
+
+                       assertEquals(List.of("keep-1", "keep-2"), 
capture.messages());
+                       assertEquals(2, capture.size());
+               }
+       }
+
+       @Test void j03_lastAndAssertHelpers_returnRawValues() {
+               var logger = RichLogger.getLogger("j03");
+               var ex = new RuntimeException("x");
+               try (var capture = logger.captureEvents()) {
+                       logger.info("a");
+                       logger.warning(ex, "b");
+
+                       assertEquals("b", capture.assertMessage());
+                       assertEquals(Level.WARNING, capture.assertLevel());
+                       assertSame(ex, capture.assertThrown());
+               }
+       }
+
+       @Test void j04_last_returnsNullWhenEmpty() {
+               var logger = RichLogger.getLogger("j04");
+               try (var capture = logger.captureEvents()) {
+                       assertNull(capture.last());
+                       assertNull(capture.assertMessage());
+                       assertNull(capture.assertLevel());
+                       assertNull(capture.assertThrown());
+               }
+       }
+
+       @Test void j05_byLevelAndMatching_areQueryTimeFilters() {
+               var logger = RichLogger.getLogger("j05");
+               try (var capture = logger.captureEvents()) {
+                       logger.info("abc-1");
+                       logger.warning("abc-2");
+                       logger.warning("xyz");
+
+                       assertEquals(2, capture.byLevel(Level.WARNING).size());
+                       assertEquals(2, capture.matching("abc").size());
+               }
+       }
 }
diff --git 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
index a68fe91aff..1caf63b2eb 100644
--- 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
+++ 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
@@ -18,6 +18,7 @@ package org.apache.juneau.commons.logging;
 
 import static org.junit.jupiter.api.Assertions.*;
 
+import java.io.*;
 import java.util.logging.*;
 import java.util.stream.*;
 
@@ -102,6 +103,45 @@ class LogRecord_Test extends TestBase {
                assertNull(rec.getParameters());
        }
 
+       @Test void c01_getMessage_defaultGenerator_usesPrintf() {
+               var rec = new LogRecord("test.logger", Level.INFO, "%s + %s = 
%s", new Object[]{1, 2, 3}, null);
+
+               assertEquals("1 + 2 = 3", rec.getMessage());
+       }
+
+       @Test void c02_getMessage_messageFormatGenerator() {
+               var rec = new LogRecord("test.logger", Level.INFO, "{0} + {1} = 
{2}", new Object[]{1, 2, 3}, null, MessageGenerator.MESSAGE_FORMAT);
+
+               assertEquals("1 + 2 = 3", rec.getMessage());
+       }
+
+       @Test void c03_getMessage_customGenerator() {
+               var rec = new LogRecord("test.logger", Level.INFO, "ignored", 
new Object[]{"a", "b"}, null, (pattern, args) -> "custom:" + args.length);
+
+               assertEquals("custom:2", rec.getMessage());
+       }
+
+       @Test void c04_getMessage_generatorFailureFallsBackToPattern() {
+               var rec = new LogRecord("test.logger", Level.INFO, null, new 
Object[]{"a"}, null, MessageGenerator.MESSAGE_FORMAT);
+
+               assertNull(rec.getMessage());
+       }
+
+       @Test void c05_constructorCompatibility_usesPrintfGenerator() {
+               var rec1 = new LogRecord("test.logger", Level.INFO, "Value: 
%s", new Object[]{42}, null);
+               var rec2 = new LogRecord("test.logger", Level.INFO, "Value: 
%s", new Object[]{42}, null, MessageGenerator.PRINTF);
+
+               assertEquals(rec1.getMessage(), rec2.getMessage());
+       }
+
+       @Test void c06_deserializedRecordFallsBackToPrintfGenerator() throws 
Exception {
+               var rec = new LogRecord("test.logger", Level.INFO, "{0}", new 
Object[]{"value"}, null, MessageGenerator.MESSAGE_FORMAT);
+               assertEquals("value", rec.getMessage());
+
+               var rec2 = roundTrip(rec);
+               assertEquals("{0}", rec2.getMessage());
+       }
+
        
//====================================================================================================
        // formatted() method - named placeholders
        
//====================================================================================================
@@ -257,4 +297,24 @@ class LogRecord_Test extends TestBase {
                assertTrue(formatted.contains("Error"));
                assertTrue(formatted.contains("\n"));
        }
+
+       @Test void f03_formatted_static_handlesPlainJulRecord() {
+               var rec = new java.util.logging.LogRecord(Level.INFO, 
"plain-jul");
+               rec.setLoggerName("test.logger");
+
+               var formatted = LogRecord.formatted(rec, 
"{logger}:{level}:{msg}");
+               assertEquals("test.logger:INFO:plain-jul", formatted);
+       }
+
+       private static LogRecord roundTrip(LogRecord record) throws Exception {
+               byte[] data;
+               try (var baos = new ByteArrayOutputStream(); var oos = new 
ObjectOutputStream(baos)) {
+                       oos.writeObject(record);
+                       oos.flush();
+                       data = baos.toByteArray();
+               }
+               try (var bais = new ByteArrayInputStream(data); var ois = new 
ObjectInputStream(bais)) {
+                       return (LogRecord)ois.readObject();
+               }
+       }
 }
diff --git 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/Logger_Test.java
 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/RichLogger_Test.java
similarity index 69%
rename from 
juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/Logger_Test.java
rename to 
juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/RichLogger_Test.java
index f1f1acbff6..ab0d6eff14 100644
--- 
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/Logger_Test.java
+++ 
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/RichLogger_Test.java
@@ -19,28 +19,30 @@ package org.apache.juneau.commons.logging;
 import static org.apache.juneau.commons.TestAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
+import java.lang.ref.*;
+import java.util.concurrent.atomic.*;
 import java.util.logging.*;
 
 import org.apache.juneau.commons.*;
 import org.junit.jupiter.api.*;
 
 /**
- * Tests for {@link Logger}.
+ * Tests for {@link RichLogger}.
  */
 @SuppressWarnings({
        "java:S117", // Local variable name intentional for test readability.
        "resource" // g01 only asserts the returned capture's initial state; 
the listener is a test fixture, not a held resource
 })
-class Logger_Test extends TestBase {
+class RichLogger_Test extends TestBase {
 
-       private static Logger getLogger(String name) {
-               var l = Logger.getLogger(name);
+       private static RichLogger getLogger(String name) {
+               var l = RichLogger.getLogger(name);
                l.setLevel(Level.OFF);
                return l;
        }
 
-       private static Logger getLogger(Class<?> class_) {
-               var l = Logger.getLogger(class_);
+       private static RichLogger getLogger(Class<?> class_) {
+               var l = RichLogger.getLogger(class_);
                l.setLevel(Level.OFF);
                return l;
        }
@@ -59,12 +61,12 @@ class Logger_Test extends TestBase {
        }
 
        @Test void a02_getLogger_byClass() {
-               var logger1 = getLogger(Logger_Test.class);
-               var logger2 = getLogger(Logger_Test.class);
+               var logger1 = getLogger(RichLogger_Test.class);
+               var logger2 = getLogger(RichLogger_Test.class);
 
                assertNotNull(logger1);
                assertSame(logger1, logger2); // Should return same instance
-               assertEquals(Logger_Test.class.getName(), logger1.getName());
+               assertEquals(RichLogger_Test.class.getName(), 
logger1.getName());
        }
 
        @Test void a03_differentNames_returnDifferentInstances() {
@@ -576,15 +578,15 @@ class Logger_Test extends TestBase {
        }
 
        
//====================================================================================================
-       // Logger delegation
+       // RichLogger delegation
        
//====================================================================================================
 
-       @Test void f01_delegatesToUnderlyingLogger() {
+       @Test void f01_delegatesToUnderlyingRichLogger() {
                var logger = getLogger("f01");
-               var underlyingLogger = 
java.util.logging.Logger.getLogger("f01");
+               var underlyingRichLogger = 
java.util.logging.Logger.getLogger("f01");
 
-               assertEquals(underlyingLogger.getName(), logger.getName());
-               assertEquals(underlyingLogger.getLevel(), logger.getLevel());
+               assertEquals(underlyingRichLogger.getName(), logger.getName());
+               assertEquals(underlyingRichLogger.getLevel(), 
logger.getLevel());
        }
 
        @Test void f02_setLevel_delegates() {
@@ -595,7 +597,7 @@ class Logger_Test extends TestBase {
        }
 
        @Test void f03_isLoggable_delegates() {
-               var logger = Logger.getLogger("f03");
+               var logger = RichLogger.getLogger("f03");
                logger.setLevel(Level.INFO);
                assertTrue(logger.isLoggable(Level.INFO));
                assertTrue(logger.isLoggable(Level.SEVERE));
@@ -629,21 +631,287 @@ class Logger_Test extends TestBase {
 
        
//====================================================================================================
        // Registry stability under high logger-name volume
-       // Regression for the Java-25 Q_logging failure: a Cache-backed 
registry cleared itself once
-       // 1000+ distinct names existed, causing getLogger(X) to return a new 
instance for a name
-       // whose class had pinned the old instance in a static final field.
+       // Regression guard for weak-valued registry behavior: a 
strongly-referenced logger
+       // must remain identity-stable despite high distinct-name volume.
        
//====================================================================================================
 
        @Test void h01_registryStable_afterManyDistinctNames() {
                // Pin the instance before flooding the registry.
-               var pinned = Logger.getLogger("h01.pinned");
+               var pinned = RichLogger.getLogger("h01.pinned");
 
                // Create enough distinct logger names to exceed the old Cache 
maxSize threshold (1000).
                for (int i = 0; i < 1500; i++)
-                       Logger.getLogger("h01.flood." + i);
+                       RichLogger.getLogger("h01.flood." + i);
 
                // The registry must still return the exact same object for the 
original name.
-               assertSame(pinned, Logger.getLogger("h01.pinned"),
-                       "Logger registry must be stable: getLogger(name) must 
always return the same instance");
+               assertSame(pinned, RichLogger.getLogger("h01.pinned"),
+                       "RichLogger registry must be stable: getLogger(name) 
must always return the same instance");
+       }
+
+       
//====================================================================================================
+       // Builder views and canonical identity
+       
//====================================================================================================
+
+       @Test void i01_builderBuild_returnsDistinctViews() {
+               var a = RichLogger.builder("i01").build();
+               var b = RichLogger.builder("i01").build();
+               var c = RichLogger.getLogger("i01");
+
+               assertNotSame(a, b);
+               assertNotSame(a, c);
+               assertNotSame(b, c);
+       }
+
+       @Test void i02_builderAndCanonical_shareCapture() {
+               var canonical = RichLogger.getLogger("i02");
+               var view = RichLogger.builder("i02").messageFormat().build();
+               try (var capture = canonical.captureEvents()) {
+                       view.info("{0}", "value");
+
+                       var records = capture.getRecords();
+                       assertSize(1, records);
+                       assertEquals("value", records.get(0).getMessage());
+               }
+       }
+
+       @Test void i03_builderDefault_usesPrintf() {
+               var view = RichLogger.builder("i03").build();
+               try (var capture = RichLogger.getLogger("i03").captureEvents()) 
{
+                       view.info("%s + %s = %s", 1, 2, 3);
+
+                       var records = capture.getRecords();
+                       assertSize(1, records);
+                       assertEquals("1 + 2 = 3", records.get(0).getMessage());
+               }
+       }
+
+       @Test void i04_builderViews_useIndependentGenerators() {
+               var printfView = RichLogger.builder("i04").printf().build();
+               var messageFormatView = 
RichLogger.builder("i04").messageFormat().build();
+               var customView = RichLogger.builder("i04").generator((pattern, 
args) -> "custom").build();
+               try (var capture = RichLogger.getLogger("i04").captureEvents()) 
{
+                       printfView.info("printf %s", "x");
+                       messageFormatView.info("message {0}", "x");
+                       customView.info("ignored %s", "x");
+
+                       var records = capture.getRecords();
+                       assertSize(3, records);
+                       assertEquals("printf x", records.get(0).getMessage());
+                       assertEquals("message x", records.get(1).getMessage());
+                       assertEquals("custom", records.get(2).getMessage());
+               }
+       }
+
+       @Test void j01_findLogger_nonCreatingLookup() {
+               var name = "j01.noncreating." + System.nanoTime();
+               assertNull(RichLogger.findLogger(name));
+               var logger = RichLogger.getLogger(name);
+               assertSame(logger, RichLogger.findLogger(name));
+       }
+
+       @Test void j02_capturePinsAncestors_whileOpen() {
+               var ancestor = RichLogger.getLogger("j02");
+               var parent = RichLogger.getLogger("j02.child");
+               var child = RichLogger.getLogger("j02.child.grandchild");
+               var ancestorRef = new WeakReference<>(ancestor);
+               var parentRef = new WeakReference<>(parent);
+               ancestor = null;
+               parent = null;
+
+               try (var capture = child.captureEvents()) {
+                       assertNotNull(ancestorRef.get());
+                       assertNotNull(parentRef.get());
+               }
+       }
+
+       @Test void j03_unreferencedCanonical_eventuallyCollectable() throws 
Exception {
+               var name = "j03.collectable." + System.nanoTime();
+               var logger = RichLogger.getLogger(name);
+               var ref = new WeakReference<>(logger);
+               logger = null;
+
+               assertTrue(awaitCollected(ref), "Expected unreferenced 
canonical to become collectable");
+       }
+
+       @Test void k01_logOverride_capturesAndPublishesOnce() {
+               var logger = RichLogger.getLogger("k01");
+               var delegate = java.util.logging.Logger.getLogger("k01");
+               var published = new AtomicInteger();
+               var h = new Handler() {
+                       @Override
+                       public void publish(java.util.logging.LogRecord record) 
{
+                               published.incrementAndGet();
+                       }
+                       @Override public void flush() {}
+                       @Override public void close() {}
+               };
+               delegate.addHandler(h);
+               try (var capture = logger.captureEvents()) {
+                       var rec = new java.util.logging.LogRecord(Level.INFO, 
"plain");
+                       rec.setLoggerName("k01");
+                       logger.log(rec);
+                       logger.info("wrapped");
+
+                       var records = capture.getRecords();
+                       assertSize(2, records);
+                       assertEquals("plain", records.get(0).getMessage());
+                       assertEquals("wrapped", records.get(1).getMessage());
+                       assertEquals(2, published.get());
+               } finally {
+                       delegate.removeHandler(h);
+               }
+       }
+
+       @Test void l01_parentCapture_observesDescendantLogs() {
+               var parent = RichLogger.getLogger("l01");
+               var child = RichLogger.getLogger("l01.child.leaf");
+               child.setLevel(Level.OFF);
+               try (var capture = parent.captureEvents()) {
+                       child.info("from-child");
+                       assertSize(1, capture.getRecords());
+                       assertEquals("from-child", 
capture.getRecords().get(0).getMessage());
+               }
+       }
+
+       @Test void l02_unrelatedAncestors_notNotified() {
+               var a = RichLogger.getLogger("l02.a");
+               var b = RichLogger.getLogger("l02.b.child");
+               try (var capture = a.captureEvents()) {
+                       b.info("unrelated");
+                       assertTrue(capture.isEmpty());
+               }
+       }
+
+       @Test void l03_emitterCanDisableParentPropagation() {
+               var parent = RichLogger.getLogger("l03");
+               var child = RichLogger.getLogger("l03.child");
+               child.setUseParentListeners(false);
+               try (var capture = parent.captureEvents()) {
+                       child.info("blocked");
+                       assertTrue(capture.isEmpty());
+               } finally {
+                       child.setUseParentListeners(true);
+               }
+       }
+
+       @Test void l04_intermediateDisable_stopsNotifyAndGuardWalk() {
+               var far = RichLogger.getLogger("l04");
+               var mid = RichLogger.getLogger("l04.mid");
+               var leaf = RichLogger.getLogger("l04.mid.leaf");
+               mid.setUseParentListeners(false);
+               leaf.setLevel(Level.OFF);
+               try (var capture = far.captureEvents()) {
+                       leaf.info("should-not-arrive");
+                       assertTrue(capture.isEmpty());
+               } finally {
+                       mid.setUseParentListeners(true);
+               }
+       }
+
+       @Test void l05_propagation_doesNotCreateAncestorCanonicals() {
+               var name = "l05.root." + System.nanoTime();
+               var leaf = RichLogger.getLogger(name + ".leaf");
+               assertNull(RichLogger.findLogger(name));
+               leaf.info("no-listeners");
+               assertNull(RichLogger.findLogger(name));
+       }
+
+       @Test void m01_supplierNotEvaluated_whenNoLoggingOrListeners() {
+               var logger = RichLogger.getLogger("m01");
+               logger.setLevel(Level.OFF);
+               var calls = new AtomicInteger();
+
+               logger.info(() -> {
+                       calls.incrementAndGet();
+                       return "value";
+               });
+
+               assertEquals(0, calls.get());
+       }
+
+       @Test void m02_supplierEvaluated_whenAncestorCaptureExists() {
+               var parent = RichLogger.getLogger("m02");
+               var child = RichLogger.getLogger("m02.child");
+               child.setLevel(Level.OFF);
+               var calls = new AtomicInteger();
+
+               try (var capture = parent.captureEvents()) {
+                       child.info(() -> {
+                               calls.incrementAndGet();
+                               return "value";
+                       });
+
+                       assertEquals(1, calls.get());
+                       assertSize(1, capture.getRecords());
+                       assertEquals("value", 
capture.getRecords().get(0).getMessage());
+               }
+       }
+
+       @Test void m03_inheritedThrowableSupplier_remainsLevelGated() {
+               var logger = RichLogger.getLogger("m03");
+               logger.setLevel(Level.OFF);
+               var calls = new AtomicInteger();
+
+               try (var capture = logger.captureEvents()) {
+                       logger.log(Level.INFO, new RuntimeException("ignored"), 
() -> {
+                               calls.incrementAndGet();
+                               return "value";
+                       });
+
+                       assertEquals(0, calls.get());
+                       assertTrue(capture.isEmpty());
+               }
+       }
+
+       @Test void m04_delegateAccessorsStillForwardToDelegate() {
+               var logger = RichLogger.getLogger("m04");
+               var delegate = java.util.logging.Logger.getLogger("m04");
+
+               logger.setLevel(Level.WARNING);
+               assertEquals(Level.WARNING, logger.getLevel());
+               assertEquals(Level.WARNING, delegate.getLevel());
+               assertFalse(logger.isLoggable(Level.INFO));
+               assertTrue(logger.isLoggable(Level.SEVERE));
+       }
+
+       @Test void n01_delegatePublication_reachesRootParentHandler() {
+               var name = "n01." + System.nanoTime();
+               var logger = RichLogger.getLogger(name);
+               var root = LogManager.getLogManager().getLogger("");
+               var published = new AtomicInteger();
+               var h = new Handler() {
+                       @Override
+                       public void publish(java.util.logging.LogRecord record) 
{
+                               if (name.equals(record.getLoggerName()))
+                                       published.incrementAndGet();
+                       }
+                       @Override public void flush() {}
+                       @Override public void close() {}
+               };
+               // Before delegate.log(record) interception existed, wrapper 
publication ended on the
+               // unregistered wrapper and never reached root handlers.
+               root.addHandler(h);
+               try (var capture = logger.captureEvents()) {
+                       var rec = new 
java.util.logging.LogRecord(Level.WARNING, "raw");
+                       rec.setLoggerName(name);
+                       logger.log(rec);
+                       logger.info("commons");
+                       logger.log(Level.WARNING, new 
RuntimeException("ignored"), () -> "supplier");
+
+                       assertSize(3, capture.getRecords());
+                       assertEquals(3, published.get());
+               } finally {
+                       root.removeHandler(h);
+               }
+       }
+
+       private static boolean awaitCollected(WeakReference<?> ref) throws 
Exception {
+               for (int i = 0; i < 20; i++) {
+                       System.gc();
+                       Thread.sleep(20);
+                       if (ref.get() == null)
+                               return true;
+               }
+               return false;
        }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
index cb9246349c..735bf505fa 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
@@ -446,7 +446,7 @@ public class WriterSerializer extends Serializer implements 
ThrowingFunction<Obj
         * @return This object.
         */
        public final WriterSerializer println(Object o) {
-               Logger.getLogger(WriterSerializer.class).info(toString(o));  // 
NOT DEBUG
+               RichLogger.getLogger(WriterSerializer.class).info(toString(o)); 
 // NOT DEBUG
                return this;
        }
 
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
index a612003885..37a107eeaa 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
@@ -231,6 +231,6 @@ public class AtomHtmlExample {
                 *     </tr>
                 *  </table>
                 */
-               Logger.getLogger(AtomHtmlExample.class).info(s.write(feed));
+               RichLogger.getLogger(AtomHtmlExample.class).info(s.write(feed));
        }
 }
\ No newline at end of file
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
index 53aeb91bb3..02f03c75a4 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
@@ -69,7 +69,7 @@ public class HtmlComplexExample {
                var flat = htmlSerializer.write(pojoc);
 
                // Print out the created POJO in JSON format.
-               Logger.getLogger(HtmlComplexExample.class).info(flat);
+               RichLogger.getLogger(HtmlComplexExample.class).info(flat);
 
                var parse = htmlParser.read(flat, PojoComplex.class);
 
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
index f6f34368fd..b2cae26141 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
@@ -46,7 +46,7 @@ public class HtmlSimpleExample {
                var flat = htmlSerializer.write(pojo);
 
                // Print out the created POJO in JSON format.
-               Logger.getLogger(HtmlSimpleExample.class).info(flat);
+               RichLogger.getLogger(HtmlSimpleExample.class).info(flat);
 
                var parse = htmlParser.read(flat, Pojo.class);
 
@@ -60,7 +60,7 @@ public class HtmlSimpleExample {
                 *  </table></div></div></article></section></body></html>
                 */
                var docSerialized = HtmlDocSerializer.DEFAULT.write(pojo);
-               Logger.getLogger(HtmlSimpleExample.class).info(docSerialized);
+               
RichLogger.getLogger(HtmlSimpleExample.class).info(docSerialized);
 
                // The object above can be parsed thanks to the 
@BeanCtor(properties = id,name) annotation on Pojo
                // Using this approach, you can keep your POJOs immutable, and 
still serialize and deserialize them.
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
index 04e083edf3..9a4fd2c12a 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
@@ -65,7 +65,7 @@ public class JsonComplexExample {
                var flat = jsonSerializer.write(pojoc);
 
                // Print out the created POJO in JSON format.
-               Logger.getLogger(JsonComplexExample.class).info(flat);
+               RichLogger.getLogger(JsonComplexExample.class).info(flat);
 
                var parse = jsonParser.read(flat, PojoComplex.class);
 
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
index 2bb59bec60..a2ef0fe367 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
@@ -49,7 +49,7 @@ public class JsonConfigurationExample {
                 */
                var withWhitespace = 
JsonSerializer.create().ws().build().write(aPojo);
                // the output will be padded with spaces after format characters
-               
Logger.getLogger(JsonConfigurationExample.class).info(withWhitespace);
+               
RichLogger.getLogger(JsonConfigurationExample.class).info(withWhitespace);
 
                /**
                 * Produces
@@ -57,7 +57,7 @@ public class JsonConfigurationExample {
                 */
                var escaped = 
JsonSerializer.create().escapeSolidus().build().write(aPojo);
                // the output will have escaped /
-               Logger.getLogger(JsonConfigurationExample.class).info(escaped);
+               
RichLogger.getLogger(JsonConfigurationExample.class).info(escaped);
 
                /**
                 * Produces
@@ -73,7 +73,7 @@ public class JsonConfigurationExample {
                        .build()
                        .write(aPojo);  // Create a Json5Serializer
 
-               
Logger.getLogger(JsonConfigurationExample.class).info(configurableJson);
+               
RichLogger.getLogger(JsonConfigurationExample.class).info(configurableJson);
 
        }
 }
\ No newline at end of file
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
index fbf509d20b..dd989590d1 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
@@ -53,7 +53,7 @@ public class JsonSimpleExample {
 
                var flat = jsonSerializer.write(pojo);
                // Print out the created POJO in JSON format.
-               Logger.getLogger(JsonSimpleExample.class).info(flat);
+               RichLogger.getLogger(JsonSimpleExample.class).info(flat);
 
                var parse = jsonParser.read(flat, Pojo.class);
 
@@ -61,7 +61,7 @@ public class JsonSimpleExample {
                assert parse.getName().equals(pojo.getName());
 
                var json5 = Json5Serializer.DEFAULT.write(pojo);
-               Logger.getLogger(JsonSimpleExample.class).info(json5);
+               RichLogger.getLogger(JsonSimpleExample.class).info(json5);
 
                // Parse a JSON object (creates a generic JsonMap).
                var json = "{name:'John Smith',age:21}";
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
index bde17bcd08..7534c0def1 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
@@ -74,7 +74,7 @@ public class OapiExample {
                // Convert POJO to BASE64-encoded string.
                var s = OpenApiSerializer.DEFAULT;
                var httpPart = s.getPartSession().write(HEADER, schemab, pojo);
-               Logger.getLogger(OapiExample.class).info(httpPart);
+               RichLogger.getLogger(OapiExample.class).info(httpPart);
 
                // Convert BASE64-encoded string back into a POJO.
                var p = OpenApiParser.DEFAULT;
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
index 5bc4f81ca4..f0a6a11c77 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
@@ -41,29 +41,29 @@ public class SvlExample {
                System.out.println(vr.resolve("JAVA_HOME=$E{JAVA_HOME, not 
defined}"));
 
                // $S{key[,default]} for getting system properties (uses 
System.getProperty() )
-               
Logger.getLogger(SvlExample.class).info(vr.resolve("os.name=$S{os.name, not 
defined}"));
+               
RichLogger.getLogger(SvlExample.class).info(vr.resolve("os.name=$S{os.name, not 
defined}"));
 
                // #{if(cond, then, else)} general if or if-else condition // 
NOSONAR
                // #{notEmpty(s)} returns true if not empty
-               Logger.getLogger(SvlExample.class).info(vr.resolve("TEST_VAR is 
#{if(#{notEmpty($E{TEST_VAR})}, not empty, empty)}"));
+               
RichLogger.getLogger(SvlExample.class).info(vr.resolve("TEST_VAR is 
#{if(#{notEmpty($E{TEST_VAR})}, not empty, empty)}"));
 
                // #{switch(value, pattern1, val1, ..., default)} glob-pattern 
switch-case // NOSONAR
                System.out.println(vr.resolve("#{switch(Carrot, *Ap*, Fruit, 
*Car*, Veg, *, N/A)}"));
 
                // #{replaceRegex(s, regex, replacement)} pattern replace
-               Logger.getLogger(SvlExample.class).info(vr.resolve("Java 
version=#{replaceRegex($S{java.version}, \"(_([0-9]+))\", \" build=$2\")}"));
+               RichLogger.getLogger(SvlExample.class).info(vr.resolve("Java 
version=#{replaceRegex($S{java.version}, \"(_([0-9]+))\", \" build=$2\")}"));
 
                // #{upper(s)} / #{lower(s)} case conversion
-               
Logger.getLogger(SvlExample.class).info(vr.resolve("#{lower(JAVA_HOME)} 
#{upper($E{JAVA_HOME})}"));
+               
RichLogger.getLogger(SvlExample.class).info(vr.resolve("#{lower(JAVA_HOME)} 
#{upper($E{JAVA_HOME})}"));
 
                // #{len(s[,delimiter])} length / part count
-               Logger.getLogger(SvlExample.class).info(vr.resolve("parts = 
#{len($S{os.version}, \".\")}, charcount = #{len($S{os.version})}"));
+               RichLogger.getLogger(SvlExample.class).info(vr.resolve("parts = 
#{len($S{os.version}, \".\")}, charcount = #{len($S{os.version})}"));
 
                // #{substring(s, start[, end])} substring extraction
-               Logger.getLogger(SvlExample.class).info(vr.resolve("version = 
#{substring($S{java.version}, 0, 3)}"));
+               RichLogger.getLogger(SvlExample.class).info(vr.resolve("version 
= #{substring($S{java.version}, 0, 3)}"));
 
                // #{extract(s, regex[, group])} regex group extraction
-               Logger.getLogger(SvlExample.class).info(vr.resolve("update = 
#{extract($S{java.version}, \"_([0-9]+)\", 1)}"));
+               RichLogger.getLogger(SvlExample.class).info(vr.resolve("update 
= #{extract($S{java.version}, \"_([0-9]+)\", 1)}"));
 
                /*
                 *  See all supported variable types at,
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
index 458bdace7e..40ad5ea864 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
@@ -58,7 +58,7 @@ public class UonComplexExample {
                 */
                var uonSerializer = UonSerializer.DEFAULT;
                // This will show the final output from the bean
-               
Logger.getLogger(UonComplexExample.class).info(uonSerializer.write(pojoc));
+               
RichLogger.getLogger(UonComplexExample.class).info(uonSerializer.write(pojoc));
 
                var obj = UonParser.DEFAULT.read(uonSerializer.write(pojoc), 
PojoComplex.class);
 
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
index 2e2b51c13d..8431bece61 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
@@ -43,7 +43,7 @@ public class UonExample {
                 * (name=name,id=id)
                 */
                var serial = UonSerializer.DEFAULT.write(pojo);
-               Logger.getLogger(UonExample.class).info(serial);
+               RichLogger.getLogger(UonExample.class).info(serial);
 
                // Deserialize back to Pojo instance
                var obj = UonParser.DEFAULT.read(serial, Pojo.class);
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
index a8d49cc776..41deb413b1 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
@@ -53,7 +53,7 @@ public class XmlComplexExample {
 
                // Serialize to human readable XML and print
                var serial = XmlSerializer.DEFAULT_SQ_READABLE.write(pojoc);
-               Logger.getLogger(XmlComplexExample.class).info(serial);
+               RichLogger.getLogger(XmlComplexExample.class).info(serial);
 
                // Deserialize back to PojoComplex instance
                var obj = XmlParser.DEFAULT.read(serial, PojoComplex.class);
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
index 4c81c08ca5..4a469c44d9 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
@@ -51,7 +51,7 @@ public class XmlConfigurationExample {
                 */
                var withWhitespace = 
XmlSerializer.create().ws().build().write(aPojo);
                // the output will be padded with spaces after format 
characters.
-               
Logger.getLogger(XmlConfigurationExample.class).info(withWhitespace);
+               
RichLogger.getLogger(XmlConfigurationExample.class).info(withWhitespace);
 
                Map<String,List<Pojo>> values = m();
                var pojoc = new PojoComplex("pojo", new Pojo("1.0", "name0"), 
values);
@@ -60,13 +60,13 @@ public class XmlConfigurationExample {
                
//<object><innerPojo><name>name0</name><id>1.0</id></innerPojo><id>pojo</id></object>
                var mapescaped = 
XmlSerializer.create().trimEmptyMaps().build().write(pojoc);
                // the output will have trimmed Empty maps.
-               
Logger.getLogger(XmlConfigurationExample.class).info(mapescaped);
+               
RichLogger.getLogger(XmlConfigurationExample.class).info(mapescaped);
 
                //Produces
                //<object 
xmlns="http://www.apache.org/2013/Juneau";><name>&lt;pojo&gt;</name><id>a</id></object>
                var nspaceToRoot = 
XmlSerializer.create().ns().addNamespaceUrisToRoot().build().write(aPojo);
                // the output will add default name space to the xml document 
root.
-               
Logger.getLogger(XmlConfigurationExample.class).info(nspaceToRoot);
+               
RichLogger.getLogger(XmlConfigurationExample.class).info(nspaceToRoot);
 
                var nPojo = new Pojo("a", null);
 
@@ -74,14 +74,14 @@ public class XmlConfigurationExample {
                //<object><id>a</id></object>
                var nullescaped = XmlSerializer.create().build().write(nPojo);
                // the output will have trimmed null properties.
-               
Logger.getLogger(XmlConfigurationExample.class).info(nullescaped);
+               
RichLogger.getLogger(XmlConfigurationExample.class).info(nullescaped);
 
                //Produces
                //<object 
xmlns="http://www.pierobon.org/iis/review1.htm.html#one";><name>&lt;pojo&gt;</name><id>a</id></object>
                var dNamsSpace = 
XmlSerializer.create().enableNamespaces().defaultNamespace(Namespace.create("http://www.pierobon.org";
 + "/iis/review1.htm.html#one")).addNamespaceUrisToRoot().build()
                        .write(aPojo);
                // the output will have new default namespace added.
-               
Logger.getLogger(XmlConfigurationExample.class).info(dNamsSpace);
+               
RichLogger.getLogger(XmlConfigurationExample.class).info(dNamsSpace);
 
        }
 }
\ No newline at end of file
diff --git 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
index c8a93fde22..b2f3f89830 100644
--- 
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
+++ 
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
@@ -46,7 +46,7 @@ public class XmlSimpleExample {
                 * </object>
                 */
                var serial = XmlSerializer.DEFAULT_SQ_READABLE.write(pojo);
-               Logger.getLogger(XmlSimpleExample.class).info(serial);
+               RichLogger.getLogger(XmlSimpleExample.class).info(serial);
 
                // Deserialize back to Pojo instance
                var obj = XmlParser.DEFAULT.read(serial, Pojo.class);
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
index 3cb2142f66..1fc65f3ca9 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
@@ -57,7 +57,7 @@ import org.apache.juneau.commons.function.*;
 import org.apache.juneau.commons.http.MediaType;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.commons.lang.*;
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
 import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.commons.reflect.ParameterInfo;
 import org.apache.juneau.commons.settings.*;
@@ -153,7 +153,7 @@ import jakarta.servlet.http.*;
 })
 public class RestContext extends Context {
 
-       private static final Logger LOG = Logger.getLogger(RestContext.class);
+       private static final RichLogger LOG = 
RichLogger.getLogger(RestContext.class);
 
        // Property name constants
        private static final String PROP_allowContentParam = 
"allowContentParam";
@@ -1043,7 +1043,7 @@ public class RestContext extends Context {
                bs.addDefaultSupplier(EncoderSet.class, encoders::get);
                bs.addDefaultSupplier(SerializerSet.class, serializers::get);
                bs.addDefaultSupplier(ParserSet.class, parsers::get);
-               bs.addDefaultSupplier(Logger.class, logger::get);
+               bs.addDefaultSupplier(RichLogger.class, logger::get);
                bs.addDefaultSupplier(java.util.logging.Logger.class, 
logger::get);
                bs.addDefaultSupplier(ThrownStore.class, thrownStore::get);
                bs.addDefaultSupplier(MethodExecStore.class, 
methodExecStore::get);
@@ -1573,15 +1573,15 @@ public class RestContext extends Context {
        private final Memoizer<JsonSchemaGenerator> jsonSchemaGenerator = 
memoizer(() -> jsonSchemaGeneratorBuilder.get().build());
 
        /**
-        * The {@link Logger} for this resource.
+        * The {@link RichLogger} for this resource.
         *
         * <p>
-        * Defaults to {@code Logger.getLogger(resourceClass.getName())}. A 
bean-store override or
+        * Defaults to {@code RichLogger.getLogger(resourceClass.getName())}. A 
bean-store override or
         * {@code @Bean} factory method REPLACES the default.
         */
-       private final Memoizer<Logger> logger = memoizer(() -> {
-               var v = Holder.of(Logger.getLogger(cn(resourceClass())));
-               beanStore().createBeanFromMethod(Logger.class, 
resource().get(), RestContext::isBeanMethod, v.get()).ifPresent(v::set);
+       private final Memoizer<RichLogger> logger = memoizer(() -> {
+               var v = Holder.of(RichLogger.getLogger(cn(resourceClass())));
+               beanStore().createBeanFromMethod(RichLogger.class, 
resource().get(), RestContext::isBeanMethod, v.get()).ifPresent(v::set);
                return v.get();
        });
 
@@ -3252,7 +3252,7 @@ public class RestContext extends Context {
        });
 
        /** Logger for async / virtual-thread setup events. Used at memoizer 
init before {@link #getLogger()} may be wired up. */
-       private static final Logger ASYNC_LOG = 
Logger.getLogger(RestContext.class.getName() + ".async");
+       private static final RichLogger ASYNC_LOG = 
RichLogger.getLogger(RestContext.class.getName() + ".async");
 
        /**
         * Whether framework memoizers and operation/child contexts should be 
force-initialized during constructor execution;
@@ -4079,7 +4079,7 @@ public class RestContext extends Context {
         *      The logger for this resource.
         *      <br>Never <jk>null</jk>.
         */
-       public Logger getLogger() { return 
beanStore.getBean(Logger.class).orElse(null); }
+       public RichLogger getLogger() { return 
beanStore.getBean(RichLogger.class).orElse(null); }
 
        /**
         * Returns the resource bundle used by this resource.
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
index e1daf7bd7d..57dc292f08 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
@@ -20,7 +20,6 @@ import static java.time.format.DateTimeFormatter.*;
 import static java.time.temporal.ChronoUnit.*;
 import static org.apache.juneau.commons.httppart.HttpPartType.*;
 import static org.apache.juneau.commons.utils.AssertionUtils.*;
-import static org.apache.juneau.commons.utils.ObjectUtils.*;
 import static org.apache.juneau.commons.utils.Shorts.*;
 import static org.apache.juneau.commons.utils.StringUtils.*;
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
index 3c8fd0bc6e..e0c43b2d21 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
@@ -22,7 +22,7 @@ import java.util.concurrent.atomic.*;
 import java.util.function.*;
 import java.util.logging.*;
 
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
 import org.apache.juneau.http.response.*;
 import org.apache.juneau.rest.server.*;
 
@@ -150,7 +150,7 @@ public abstract class RestResource {
                var c = context.get();
                var logger = c == null ? null : c.getLogger();
                if (logger == null)
-                       logger = Logger.getLogger(cn(this));
+                       logger = RichLogger.getLogger(cn(this));
                logger.log(level, cause, msg);
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
index 9e16199f2b..3b414d8953 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
@@ -28,7 +28,7 @@ import java.util.function.*;
 import java.util.logging.*;
 
 import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
 import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.commons.utils.*;
 import org.apache.juneau.http.response.*;
@@ -366,7 +366,7 @@ public abstract class RestServlet extends HttpServlet {
                var c = context.get();
                var logger = c == null ? null : c.getLogger();
                if (logger == null)
-                       logger = Logger.getLogger(cn(this));
+                       logger = RichLogger.getLogger(cn(this));
                logger.log(level, cause, msg);
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
index 4877c6d7b8..c57d11b820 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
@@ -72,7 +72,7 @@ import jakarta.servlet.*;
 })
 public class BasicSwaggerProviderSession {
 
-       private static final Logger LOG = 
Logger.getLogger(BasicSwaggerProviderSession.class);
+       private static final RichLogger LOG = 
RichLogger.getLogger(BasicSwaggerProviderSession.class);
 
        // Swagger JSON property name constants
        private static final String SWAGGER_allOf = "allOf";
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
index b5aed4b270..50719b7688 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
@@ -30,6 +30,9 @@ import jakarta.servlet.http.*;
  * be rendered. Memory stays bounded even for large uploads.
  *
  */
+@SuppressWarnings({
+       "resource" // The tee'd request stream is owned by the underlying 
request/servlet container, which closes it when the request completes; closing 
it here would break body-caching. Eclipse JDT @Owning warning is by design.
+})
 public class CachingHttpServletRequest extends HttpServletRequestWrapper {
 
        /** Default body capture cap, in bytes (8&nbsp;KB). */
diff --git 
a/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
 
b/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
index a7df635de2..bc99998870 100644
--- 
a/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
+++ 
b/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
@@ -243,10 +243,10 @@ public class GitControl implements AutoCloseable {
                try {
                        var it = pc.call().iterator();
                        if (it.hasNext()) {
-                               Logger.getLogger(GitControl.class).info("{}", 
it.next().toString());
+                               
RichLogger.getLogger(GitControl.class).info("{}", it.next().toString());
                        }
                } catch (InvalidRemoteException e) {
-                       Logger.getLogger(GitControl.class).warning(e, "Error 
pushing to remote repository.");
+                       RichLogger.getLogger(GitControl.class).warning(e, 
"Error pushing to remote repository.");
                }
        }
 }
\ No newline at end of file
diff --git 
a/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
 
b/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
index a7bb48426e..a9db75ae8f 100644
--- 
a/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
+++ 
b/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
@@ -47,17 +47,23 @@ class GitControl_Test {
        // 
-----------------------------------------------------------------------------------------------------------------
 
        @Test void a01_nullUsernameYieldsAnonymousTransport() {
-               assertNull(GitControl.findCredentialsProvider(null, 
"GIT_TOKEN", new BasicBeanStore()));
+               try (var beanStore = new BasicBeanStore()) {
+                       assertNull(GitControl.findCredentialsProvider(null, 
"GIT_TOKEN", beanStore));
+               }
        }
 
+       @SuppressWarnings({
+               "resource" // new BasicBeanStore().addBean(...) is a fluent 
self-return; the try-with-resources on 'beanStore' already closes the same 
instance the constructor produced.
+       })
        @Test void a02_contributedStoreResolvesSecret() throws Exception {
                var store = new InMemorySecretStore();
                store.store("GIT_TOKEN", "hunter2".toCharArray());
-               var beanStore = new BasicBeanStore().addBean(SecretStore.class, 
store);
 
-               var cp = GitControl.findCredentialsProvider("svc-account", 
"GIT_TOKEN", beanStore);
-               assertInstanceOf(UsernamePasswordCredentialsProvider.class, cp);
-               assertArrayEquals("hunter2".toCharArray(), passwordOf(cp));
+               try (var beanStore = new 
BasicBeanStore().addBean(SecretStore.class, store)) {
+                       var cp = 
GitControl.findCredentialsProvider("svc-account", "GIT_TOKEN", beanStore);
+                       
assertInstanceOf(UsernamePasswordCredentialsProvider.class, cp);
+                       assertArrayEquals("hunter2".toCharArray(), 
passwordOf(cp));
+               }
        }
 
        @Test void a03_absentSecretYieldsEmptyPassword() throws Exception {
@@ -68,20 +74,25 @@ class GitControl_Test {
        }
 
        @Test void a04_emptyBeanStoreFallsBackToInMemoryDefault() throws 
Exception {
-               var cp = GitControl.findCredentialsProvider("svc-account", 
"GIT_TOKEN", new BasicBeanStore());
-               assertArrayEquals(new char[0], passwordOf(cp));
+               try (var beanStore = new BasicBeanStore()) {
+                       var cp = 
GitControl.findCredentialsProvider("svc-account", "GIT_TOKEN", beanStore);
+                       assertArrayEquals(new char[0], passwordOf(cp));
+               }
        }
 
        // 
-----------------------------------------------------------------------------------------------------------------
        // Constructor integration.
        // 
-----------------------------------------------------------------------------------------------------------------
 
+       @SuppressWarnings({
+               "resource" // new BasicBeanStore().addBean(...) is a fluent 
self-return; the try-with-resources on 'beanStore' already closes the same 
instance the constructor produced.
+       })
        @Test void 
b01_secretStoreConstructorWiresCredentialsAndClosesCleanly(@TempDir File dir) 
throws IOException {
                var store = new InMemorySecretStore();
                store.store("GIT_TOKEN", "hunter2".toCharArray());
-               var beanStore = new BasicBeanStore().addBean(SecretStore.class, 
store);
 
-               try (var gitControl = new GitControl(dir.getAbsolutePath(), 
"https://example.com/repo.git";, "svc-account", "GIT_TOKEN", beanStore, false)) {
+               try (var beanStore = new 
BasicBeanStore().addBean(SecretStore.class, store);
+                               var gitControl = new 
GitControl(dir.getAbsolutePath(), "https://example.com/repo.git";, 
"svc-account", "GIT_TOKEN", beanStore, false)) {
                        assertNotNull(gitControl);
                }
        }

Reply via email to