You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2024/09/09 18:25:30 UTC

(logging-log4j2) 01/02: Update `log4j-jul` to Java 17

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

pkarwasz pushed a commit to branch feature/main/split-jul
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit cde1fbc4c29cd3a04e1dba28f55c8e1d8161ea35
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Mon Sep 9 11:22:05 2024 +0200

    Update `log4j-jul` to Java 17
    
    We update `log4j-jul` to:
    
    - implement methods introduced in Java 9,
    - remove methods deprecated in Java 9.
---
 log4j-jul/pom.xml                                  |  37 +--
 .../logging/log4j/jul/AbstractLoggerAdapter.java   |  17 +-
 .../org/apache/logging/log4j/jul/ApiLogger.java    | 315 ++++++++++++++++-----
 .../org/apache/logging/log4j/jul/LogManager.java   |  22 +-
 .../apache/logging/log4j/jul/WrappedLogger.java    |   8 +
 .../logging/log4j/jul/test/ApiLoggerTest.java      |   5 -
 .../log4j/jul/test/CallerInformationTest.java      |  41 ++-
 .../logging/log4j/jul/test/ResourceBundleTest.java |  91 ++++++
 ...calling-class.xml => CallerInformationTest.xml} |  18 +-
 .../test/resources/ResourceBundleTest.properties   |  21 ++
 ...j2-calling-class.xml => ResourceBundleTest.xml} |  23 +-
 11 files changed, 456 insertions(+), 142 deletions(-)

diff --git a/log4j-jul/pom.xml b/log4j-jul/pom.xml
index 00f55786df..888dd58a5c 100644
--- a/log4j-jul/pom.xml
+++ b/log4j-jul/pom.xml
@@ -28,57 +28,48 @@
   <name>Apache Log4j JUL Adapter</name>
   <description>The Apache Log4j implementation of java.util.logging</description>
 
-  <properties>
-    <log4jParentDir>${basedir}/..</log4jParentDir>
-
-    <!--
-      ~ OSGi and JPMS options
-      -->
-    <bnd-extra-package-options>
-      <!-- Log4j Core is optional -->
-      org.apache.logging.log4j.core.*;resolution:=optional
-    </bnd-extra-package-options>
-
-    <!-- PTS requires using the JUnit Platform, which interferes with JUL initialization -->
-    <predictive.test.selection.enabled>false</predictive.test.selection.enabled>
-  </properties>
-
   <dependencies>
+
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-api</artifactId>
     </dependency>
+
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
-      <optional>true</optional>
+      <artifactId>log4j-kit</artifactId>
     </dependency>
+
     <dependency>
       <groupId>org.assertj</groupId>
       <artifactId>assertj-core</artifactId>
       <scope>test</scope>
     </dependency>
-    <!-- Required for AsyncLogger testing -->
-    <dependency>
-      <groupId>com.lmax</groupId>
-      <artifactId>disruptor</artifactId>
-      <scope>test</scope>
-    </dependency>
+
     <dependency>
       <groupId>org.hamcrest</groupId>
       <artifactId>hamcrest</artifactId>
       <scope>test</scope>
     </dependency>
+
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-async-logger</artifactId>
       <scope>test</scope>
     </dependency>
+
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-core-test</artifactId>
diff --git a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/AbstractLoggerAdapter.java b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/AbstractLoggerAdapter.java
index 7c33636a36..5b4d7f5c51 100644
--- a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/AbstractLoggerAdapter.java
+++ b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/AbstractLoggerAdapter.java
@@ -22,9 +22,22 @@ import org.apache.logging.log4j.spi.LoggerContext;
 import org.apache.logging.log4j.util.StackLocatorUtil;
 
 /**
- * Abstract Logger registry. Due to the optionality of using log4j-core, there are two registries available at runtime
- * to create: {@link ApiLoggerAdapter} and {@link CoreLoggerAdapter}.
+ * Abstract Logger registry.
+ * <p>
+ *     JUL contains methods, such as {@link Logger#setLevel}, which modify the configuration of the logging backend.
+ *     To fully implement all {@code Logger} methods, we need to provide a different {@code Logger} implementation
+ *     for each Log4j API implementation.
+ * </p>
+ * <p>
+ *     Older Log4j versions provided an alternative {@code CoreLoggerAdapter} implementation that supported
+ *     the modification of Log4j Core configuration using JUL.
+ * </p>
+ *     Since version 2.24.0, however, this implementation was deprecated for removal.
+ *     If you wish to enable this feature again, you need to implement this class and provide its FQCN
+ *     as {@code log4j.jul.loggerAdapter} configuration property.
+ * </p>
  *
+ * @see <a href="https://github.com/apache/logging-log4j2/issues/2353">Issue #2353</a>
  * @since 2.1
  */
 public abstract class AbstractLoggerAdapter extends org.apache.logging.log4j.spi.AbstractLoggerAdapter<Logger> {
diff --git a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java
index eee946fc8c..fc4d4d38a3 100644
--- a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java
+++ b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java
@@ -16,12 +16,15 @@
  */
 package org.apache.logging.log4j.jul;
 
-import java.security.AccessController;
-import java.security.PrivilegedAction;
+import static org.apache.logging.log4j.jul.LevelTranslator.toLevel;
+
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
 import java.util.logging.Filter;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import java.util.logging.Logger;
+import org.apache.logging.log4j.message.LocalizedMessage;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.MessageFactory;
 import org.apache.logging.log4j.spi.ExtendedLogger;
@@ -49,11 +52,7 @@ public class ApiLogger extends Logger {
     ApiLogger(final ExtendedLogger logger) {
         super(logger.getName(), null);
         final Level javaLevel = LevelTranslator.toJavaLevel(logger.getLevel());
-        // "java.util.logging.LoggingPermission" "control"
-        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
-            ApiLogger.super.setLevel(javaLevel);
-            return null;
-        });
+        super.setLevel(javaLevel);
         this.logger = new WrappedLogger(logger);
     }
 
@@ -62,7 +61,7 @@ public class ApiLogger extends Logger {
         if (isFiltered(record)) {
             return;
         }
-        final org.apache.logging.log4j.Level level = LevelTranslator.toLevel(record.getLevel());
+        final org.apache.logging.log4j.Level level = toLevel(record.getLevel());
         final Object[] parameters = record.getParameters();
         final MessageFactory messageFactory = logger.getMessageFactory();
         final Message message = parameters == null
@@ -80,7 +79,7 @@ public class ApiLogger extends Logger {
 
     @Override
     public boolean isLoggable(final Level level) {
-        return logger.isEnabled(LevelTranslator.toLevel(level));
+        return logger.isEnabled(toLevel(level));
     }
 
     @Override
@@ -116,45 +115,111 @@ public class ApiLogger extends Logger {
         throw new UnsupportedOperationException("Cannot set parent logger");
     }
 
+    private org.apache.logging.log4j.util.Supplier<String> toLog4jSupplier(Supplier<String> msgSupplier) {
+        return msgSupplier::get;
+    }
+
+    private org.apache.logging.log4j.util.Supplier<Message> toMessageSupplier(Supplier<String> msgSupplier) {
+        return () -> logger.getMessageFactory().newMessage(msgSupplier.get());
+    }
+
+    private org.apache.logging.log4j.util.Supplier<LocalizedMessage> toLocalizedMessageSupplier(
+            ResourceBundle bundle, String msg) {
+        return () -> new LocalizedMessage(bundle, msg);
+    }
+
+    private org.apache.logging.log4j.util.Supplier<LocalizedMessage> toLocalizedMessageSupplier(
+            ResourceBundle bundle, String msg, Object[] params) {
+        return () -> new LocalizedMessage(bundle, msg, params);
+    }
+
+    private StackTraceElement toLocation(String sourceClass, String sourceMethod) {
+        return new StackTraceElement(sourceClass, sourceMethod, null, 0);
+    }
+
     @Override
     public void log(final Level level, final String msg) {
-        if (getFilter() == null) {
-            logger.log(LevelTranslator.toLevel(level), msg);
-        } else {
+        if (hasFilter()) {
             super.log(level, msg);
+        } else {
+            logger.log(toLevel(level), msg);
         }
     }
 
+    /**
+     * @since 3.0.0
+     */
     @Override
-    public void log(final Level level, final String msg, final Object param1) {
-        if (getFilter() == null) {
-            logger.log(LevelTranslator.toLevel(level), msg, param1);
+    public void log(Level level, Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.log(level, msgSupplier);
         } else {
+            logger.log(toLevel(level), toLog4jSupplier(msgSupplier));
+        }
+    }
+
+    @Override
+    public void log(final Level level, final String msg, final Object param1) {
+        if (hasFilter()) {
             super.log(level, msg, param1);
+        } else {
+            logger.log(toLevel(level), msg, param1);
         }
     }
 
     @Override
     public void log(final Level level, final String msg, final Object[] params) {
-        if (getFilter() == null) {
-            logger.log(LevelTranslator.toLevel(level), msg, params);
-        } else {
+        if (hasFilter()) {
             super.log(level, msg, params);
+        } else {
+            logger.log(toLevel(level), msg, params);
         }
     }
 
     @Override
     public void log(final Level level, final String msg, final Throwable thrown) {
-        if (getFilter() == null) {
-            logger.log(LevelTranslator.toLevel(level), msg, thrown);
-        } else {
+        if (hasFilter()) {
             super.log(level, msg, thrown);
+        } else {
+            logger.log(toLevel(level), msg, thrown);
+        }
+    }
+
+    /**
+     * @since 3.0.0
+     */
+    @Override
+    public void log(Level level, Throwable thrown, Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.log(level, thrown, msgSupplier);
+        } else {
+            logger.log(toLevel(level), toLog4jSupplier(msgSupplier), thrown);
         }
     }
 
     @Override
     public void logp(final Level level, final String sourceClass, final String sourceMethod, final String msg) {
-        log(level, msg);
+        if (hasFilter()) {
+            super.logp(level, sourceClass, sourceMethod, msg);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .log(msg);
+        }
+    }
+
+    /**
+     * @since 3.0.0
+     */
+    @Override
+    public void logp(Level level, String sourceClass, String sourceMethod, Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.logp(level, sourceClass, sourceMethod, msgSupplier);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .log(toMessageSupplier(msgSupplier));
+        }
     }
 
     @Override
@@ -164,7 +229,13 @@ public class ApiLogger extends Logger {
             final String sourceMethod,
             final String msg,
             final Object param1) {
-        log(level, msg, param1);
+        if (hasFilter()) {
+            super.logp(level, sourceClass, sourceMethod, msg, param1);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .log(msg, param1);
+        }
     }
 
     @Override
@@ -174,7 +245,13 @@ public class ApiLogger extends Logger {
             final String sourceMethod,
             final String msg,
             final Object[] params) {
-        log(level, msg, params);
+        if (hasFilter()) {
+            super.logp(level, sourceClass, sourceMethod, msg, params);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .log(msg, params);
+        }
     }
 
     @Override
@@ -184,50 +261,82 @@ public class ApiLogger extends Logger {
             final String sourceMethod,
             final String msg,
             final Throwable thrown) {
-        log(level, msg, thrown);
+        if (hasFilter()) {
+            super.logp(level, sourceClass, sourceMethod, msg, thrown);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .withThrowable(thrown)
+                    .log(msg);
+        }
     }
 
+    /**
+     * @since 3.0.0
+     */
     @Override
-    public void logrb(
-            final Level level,
-            final String sourceClass,
-            final String sourceMethod,
-            final String bundleName,
-            final String msg) {
-        log(level, msg);
+    public void logp(
+            Level level, String sourceClass, String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.logp(level, sourceClass, sourceMethod, thrown, msgSupplier);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .withThrowable(thrown)
+                    .log(toMessageSupplier(msgSupplier));
+        }
     }
 
+    /**
+     * @since 3.0.0
+     */
     @Override
     public void logrb(
-            final Level level,
-            final String sourceClass,
-            final String sourceMethod,
-            final String bundleName,
-            final String msg,
-            final Object param1) {
-        log(level, msg, param1);
+            Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Object... params) {
+        if (hasFilter()) {
+            super.logrb(level, sourceClass, sourceMethod, bundle, msg, params);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .log(toLocalizedMessageSupplier(bundle, msg, params));
+        }
     }
 
     @Override
     public void logrb(
-            final Level level,
-            final String sourceClass,
-            final String sourceMethod,
-            final String bundleName,
-            final String msg,
-            final Object[] params) {
-        log(level, msg, params);
+            Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {
+        if (hasFilter()) {
+            super.logrb(level, sourceClass, sourceMethod, bundle, msg, thrown);
+        } else {
+            logger.atLevel(toLevel(level))
+                    .withLocation(toLocation(sourceClass, sourceMethod))
+                    .withThrowable(thrown)
+                    .log(toLocalizedMessageSupplier(bundle, msg));
+        }
     }
 
+    /**
+     * @since 3.0.0
+     */
     @Override
-    public void logrb(
-            final Level level,
-            final String sourceClass,
-            final String sourceMethod,
-            final String bundleName,
-            final String msg,
-            final Throwable thrown) {
-        log(level, msg, thrown);
+    public void logrb(Level level, ResourceBundle bundle, String msg, Object... params) {
+        if (hasFilter()) {
+            super.logrb(level, bundle, msg, params);
+        } else {
+            logger.log(toLevel(level), toLocalizedMessageSupplier(bundle, msg, params));
+        }
+    }
+
+    /**
+     * @since 3.0.0
+     */
+    @Override
+    public void logrb(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
+        if (hasFilter()) {
+            super.logrb(level, bundle, msg, thrown);
+        } else {
+            logger.atLevel(toLevel(level)).withThrowable(thrown).log(toLocalizedMessageSupplier(bundle, msg));
+        }
     }
 
     @Override
@@ -262,64 +371,134 @@ public class ApiLogger extends Logger {
 
     @Override
     public void severe(final String msg) {
-        if (getFilter() == null) {
+        if (hasFilter()) {
+            super.severe(msg);
+        } else {
             logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.ERROR, null, msg);
+        }
+    }
+
+    /**
+     * @since 3.0.0
+     */
+    @Override
+    public void severe(Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.severe(msgSupplier);
         } else {
-            super.severe(msg);
+            logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.ERROR, null, toMessageSupplier(msgSupplier), null);
         }
     }
 
     @Override
     public void warning(final String msg) {
-        if (getFilter() == null) {
+        if (hasFilter()) {
+            super.warning(msg);
+        } else {
             logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.WARN, null, msg);
+        }
+    }
+
+    @Override
+    public void warning(Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.warning(msgSupplier);
         } else {
-            super.warning(msg);
+            logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.WARN, null, toMessageSupplier(msgSupplier), null);
         }
     }
 
     @Override
     public void info(final String msg) {
-        if (getFilter() == null) {
+        if (hasFilter()) {
+            super.info(msg);
+        } else {
             logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.INFO, null, msg);
+        }
+    }
+
+    @Override
+    public void info(Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.info(msgSupplier);
         } else {
-            super.info(msg);
+            logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.INFO, null, toMessageSupplier(msgSupplier), null);
         }
     }
 
     @Override
     public void config(final String msg) {
-        if (getFilter() == null) {
+        if (hasFilter()) {
+            super.config(msg);
+        } else {
             logger.logIfEnabled(FQCN, LevelTranslator.CONFIG, null, msg);
+        }
+    }
+
+    @Override
+    public void config(Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.config(msgSupplier);
         } else {
-            super.config(msg);
+            logger.logIfEnabled(FQCN, LevelTranslator.CONFIG, null, toMessageSupplier(msgSupplier), null);
         }
     }
 
     @Override
     public void fine(final String msg) {
-        if (getFilter() == null) {
+        if (hasFilter()) {
+            super.fine(msg);
+        } else {
             logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.DEBUG, null, msg);
+        }
+    }
+
+    @Override
+    public void fine(Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.fine(msgSupplier);
         } else {
-            super.fine(msg);
+            logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.DEBUG, null, toMessageSupplier(msgSupplier), null);
         }
     }
 
     @Override
     public void finer(final String msg) {
-        if (getFilter() == null) {
+        if (hasFilter()) {
+            super.finer(msg);
+        } else {
             logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.TRACE, null, msg);
+        }
+    }
+
+    @Override
+    public void finer(Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.finer(msgSupplier);
         } else {
-            super.finer(msg);
+            logger.logIfEnabled(FQCN, org.apache.logging.log4j.Level.TRACE, null, toMessageSupplier(msgSupplier), null);
         }
     }
 
     @Override
     public void finest(final String msg) {
-        if (getFilter() == null) {
+        if (hasFilter()) {
+            super.finest(msg);
+        } else {
             logger.logIfEnabled(FQCN, LevelTranslator.FINEST, null, msg);
+        }
+    }
+
+    @Override
+    public void finest(Supplier<String> msgSupplier) {
+        if (hasFilter()) {
+            super.finest(msgSupplier);
         } else {
-            super.finest(msg);
+            logger.logIfEnabled(FQCN, LevelTranslator.FINEST, null, toMessageSupplier(msgSupplier), null);
         }
     }
+
+    private boolean hasFilter() {
+        return getFilter() != null;
+    }
 }
diff --git a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java
index b92d87bdf8..2bcadf4df1 100644
--- a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java
+++ b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java
@@ -21,7 +21,6 @@ import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.logging.Logger;
-import org.apache.logging.log4j.LoggingException;
 import org.apache.logging.log4j.kit.env.PropertyEnvironment;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
@@ -41,9 +40,6 @@ import org.apache.logging.log4j.util.LoaderUtil;
 public class LogManager extends java.util.logging.LogManager {
 
     private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
-    private static final String CORE_LOGGER_CLASS_NAME = "org.apache.logging.log4j.core.Logger";
-    private static final String CORE_LOGGER_ADAPTER_CLASS_NAME = "org.apache.logging.log4j.jul.CoreLoggerAdapter";
-    private static final String API_LOGGER_ADAPTER_CLASS_NAME = "org.apache.logging.log4j.jul.ApiLoggerAdapter";
     private final AbstractLoggerAdapter loggerAdapter;
     // Contains the set of logger names that are actively being requested using getLogger.
     private final ThreadLocal<Set<String>> recursive = ThreadLocal.withInitial(HashSet::new);
@@ -62,21 +58,9 @@ public class LogManager extends java.util.logging.LogManager {
             }
         }
         if (adapter == null) {
-            // default adapter
-            String adapterClassName;
-            try {
-                // find out if log4j-core is available
-                LoaderUtil.loadClass(CORE_LOGGER_CLASS_NAME);
-                adapterClassName = CORE_LOGGER_ADAPTER_CLASS_NAME;
-            } catch (final ClassNotFoundException ignored) {
-                adapterClassName = API_LOGGER_ADAPTER_CLASS_NAME;
-            }
-            LOGGER.debug("Attempting to use {}", adapterClassName);
-            try {
-                adapter = LoaderUtil.newCheckedInstanceOf(adapterClassName, AbstractLoggerAdapter.class);
-            } catch (final Exception e) {
-                throw LOGGER.throwing(new LoggingException(e));
-            }
+            // Use API by default
+            // See https://github.com/apache/logging-log4j2/issues/2353
+            adapter = new ApiLoggerAdapter();
         }
         loggerAdapter = adapter;
         LOGGER.info("Registered Log4j as the java.util.logging.LogManager.");
diff --git a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/WrappedLogger.java b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/WrappedLogger.java
index 8c5d035628..4ef6009985 100644
--- a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/WrappedLogger.java
+++ b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/WrappedLogger.java
@@ -17,7 +17,9 @@
 package org.apache.logging.log4j.jul;
 
 import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.message.EntryMessage;
+import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.spi.ExtendedLogger;
 import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;
 
@@ -64,4 +66,10 @@ class WrappedLogger extends ExtendedLoggerWrapper {
     public <T extends Throwable> T throwing(final T t) {
         return throwing(FQCN, LevelTranslator.toLevel(java.util.logging.Level.FINER), t);
     }
+
+    @Override
+    protected void log(
+            Level level, Marker marker, String fqcn, StackTraceElement location, Message message, Throwable throwable) {
+        logger.logMessage(level, marker, fqcn, location, message, throwable);
+    }
 }
diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java
index ce58daf202..49fb19d2b5 100644
--- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java
+++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java
@@ -16,16 +16,13 @@
  */
 package org.apache.logging.log4j.jul.test;
 
-import static org.apache.logging.log4j.jul.test.JulTestProperties.JUL_LOGGER_ADAPTER;
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 
 import java.util.logging.Logger;
-import org.apache.logging.log4j.core.test.TestConstants;
 import org.apache.logging.log4j.core.test.appender.ListAppender;
-import org.apache.logging.log4j.jul.ApiLoggerAdapter;
 import org.apache.logging.log4j.jul.LogManager;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -40,13 +37,11 @@ public class ApiLoggerTest extends AbstractLoggerTest {
     @BeforeClass
     public static void setUpClass() {
         System.setProperty("java.util.logging.manager", LogManager.class.getName());
-        oldAdapter = TestConstants.setSystemProperty(JUL_LOGGER_ADAPTER, ApiLoggerAdapter.class.getName());
     }
 
     @AfterClass
     public static void tearDownClass() {
         System.clearProperty("java.util.logging.manager");
-        TestConstants.setSystemProperty(JUL_LOGGER_ADAPTER, oldAdapter);
     }
 
     @Before
diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java
index ff644a40d3..ce8c981f06 100644
--- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java
+++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.jul.test;
 import static org.junit.Assert.assertEquals;
 
 import java.util.List;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.logging.log4j.core.test.appender.ListAppender;
 import org.apache.logging.log4j.core.test.junit.LoggerContextRule;
@@ -30,11 +31,13 @@ import org.junit.Test;
 
 public class CallerInformationTest {
 
-    // config from log4j-core test-jar
-    private static final String CONFIG = "log4j2-calling-class.xml";
+    private static final String PARAM_1 = "PARAM_1";
+    private static final String[] PARAMS = {PARAM_1, "PARAM_2"};
+    private static final String SOURCE_CLASS = "SourceClass";
+    private static final String SOURCE_METHOD = "sourceMethod";
 
     @Rule
-    public final LoggerContextRule ctx = new LoggerContextRule(CONFIG);
+    public final LoggerContextRule ctx = new LoggerContextRule("CallerInformationTest.xml");
 
     @BeforeClass
     public static void setUpClass() {
@@ -53,11 +56,25 @@ public class CallerInformationTest {
         logger.info("Ignored message contents.");
         logger.warning("Verifying the caller class is still correct.");
         logger.severe("Hopefully nobody breaks me!");
-        final List<String> messages = app.getMessages();
+        List<String> messages = app.getMessages();
         assertEquals("Incorrect number of messages.", 3, messages.size());
         for (final String message : messages) {
             assertEquals("Incorrect caller class name.", this.getClass().getName(), message);
         }
+
+        // Test passing the location information directly
+        app.clear();
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello!");
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello {1}!", PARAM_1);
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello {1} and {2}!", PARAMS);
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello!", new RuntimeException());
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, () -> "Hello" + PARAM_1 + "!");
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, new RuntimeException(), () -> "Hello " + PARAM_1 + "!");
+        messages = app.getMessages();
+        assertEquals("Incorrect number of messages.", 6, messages.size());
+        for (final String message : messages) {
+            assertEquals("Incorrect caller class name.", SOURCE_CLASS, message);
+        }
     }
 
     @Test
@@ -69,10 +86,24 @@ public class CallerInformationTest {
         logger.severe("ZOMBIES!!!");
         logger.warning("brains~~~");
         logger.info("Itchy. Tasty.");
-        final List<String> messages = app.getMessages();
+        List<String> messages = app.getMessages();
         assertEquals("Incorrect number of messages.", 5, messages.size());
         for (final String message : messages) {
             assertEquals("Incorrect caller method name.", "testMethodLogger", message);
         }
+
+        // Test passing the location information directly
+        app.clear();
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello!");
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello {1}!", PARAM_1);
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello {1} and {2}!", PARAMS);
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, "Hello!", new RuntimeException());
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, () -> "Hello " + PARAM_1 + "!");
+        logger.logp(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, new RuntimeException(), () -> "Hello " + PARAM_1 + "!");
+        messages = app.getMessages();
+        assertEquals("Incorrect number of messages.", 6, messages.size());
+        for (final String message : messages) {
+            assertEquals("Incorrect caller method name.", SOURCE_METHOD, message);
+        }
     }
 }
diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ResourceBundleTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ResourceBundleTest.java
new file mode 100644
index 0000000000..7431759751
--- /dev/null
+++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ResourceBundleTest.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.jul.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextRule;
+import org.apache.logging.log4j.jul.LogManager;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test methods that accept a resource bundle
+ */
+public class ResourceBundleTest {
+
+    private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("ResourceBundleTest");
+    private static final String SOURCE_CLASS = "SourceClass";
+    private static final String SOURCE_METHOD = "sourceMethod";
+
+    private static final String[] EXPECTED_MESSAGES = {"Hello!", "Hello Log4j and JUL!"};
+    private static final String[] EXPECTED_CLASS_NAMES = {ResourceBundleTest.class.getName(), SOURCE_CLASS};
+    private static final String[] EXPECTED_METHOD_NAMES = {"testCorrectMessageAndLocation", SOURCE_METHOD};
+
+    @Rule
+    public final LoggerContextRule ctx = new LoggerContextRule("ResourceBundleTest.xml");
+
+    @BeforeClass
+    public static void setUpClass() {
+        System.setProperty("java.util.logging.manager", LogManager.class.getName());
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        System.clearProperty("java.util.logging.manager");
+    }
+
+    @Test
+    public void testCorrectMessageAndLocation() {
+        ListAppender appender = ctx.getListAppender("LIST").clear();
+        Logger logger = Logger.getLogger(ResourceBundleTest.class.getName());
+
+        Throwable thrown = new RuntimeException();
+        logger.logrb(Level.INFO, BUNDLE, "msg_1", thrown);
+        logger.logrb(Level.INFO, BUNDLE, "msg_2", "Log4j", "JUL");
+        logger.logrb(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, BUNDLE, "msg_1", thrown);
+        logger.logrb(Level.INFO, SOURCE_CLASS, SOURCE_METHOD, BUNDLE, "msg_2", "Log4j", "JUL");
+
+        LogEvent[] logEvents = appender.getEvents().toArray(LogEvent[]::new);
+        for (int idx = 0; idx < logEvents.length; ++idx) {
+            assertEquals(
+                    String.format("Message of event %d", idx),
+                    EXPECTED_MESSAGES[idx % 2],
+                    logEvents[idx].getMessage().getFormattedMessage());
+            assertEquals(
+                    String.format("Source class of event %d", idx),
+                    EXPECTED_CLASS_NAMES[idx / 2],
+                    logEvents[idx].getSource().getClassName());
+            assertEquals(
+                    String.format("Source method of event %d", idx),
+                    EXPECTED_METHOD_NAMES[idx / 2],
+                    logEvents[idx].getSource().getMethodName());
+            assertSame(
+                    String.format("Exception of event %d", idx),
+                    idx % 2 == 0 ? thrown : null,
+                    logEvents[idx].getThrown());
+        }
+    }
+}
diff --git a/log4j-jul/src/test/resources/log4j2-calling-class.xml b/log4j-jul/src/test/resources/CallerInformationTest.xml
similarity index 65%
copy from log4j-jul/src/test/resources/log4j2-calling-class.xml
copy to log4j-jul/src/test/resources/CallerInformationTest.xml
index 1af22eaa82..4a73dddcc7 100644
--- a/log4j-jul/src/test/resources/log4j2-calling-class.xml
+++ b/log4j-jul/src/test/resources/CallerInformationTest.xml
@@ -15,22 +15,28 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<Configuration name="CallerInformationTest" status="ERROR">
+<Configuration xmlns="https://logging.apache.org/xml/ns"
+               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+               xsi:schemaLocation="
+                   https://logging.apache.org/xml/ns
+                   https://logging.apache.org/xml/ns/log4j-config-3.xsd">
   <Appenders>
     <List name="Class">
-      <PatternLayout pattern="%class"/>
+      <PatternLayout pattern="%class"
+                     alwaysWriteExceptions="false"/>
     </List>
     <List name="Method">
-      <PatternLayout pattern="%method"/>
+      <PatternLayout pattern="%method"
+                     alwaysWriteExceptions="false"/>
     </List>
   </Appenders>
   <Loggers>
-    <Logger name="ClassLogger" level="info">
+    <Logger name="ClassLogger" level="INFO">
       <AppenderRef ref="Class"/>
     </Logger>
-    <Logger name="MethodLogger" level="info">
+    <Logger name="MethodLogger" level="INFO">
       <AppenderRef ref="Method"/>
     </Logger>
-    <Root level="off"/>
+    <Root level="OFF"/>
   </Loggers>
 </Configuration>
diff --git a/log4j-jul/src/test/resources/ResourceBundleTest.properties b/log4j-jul/src/test/resources/ResourceBundleTest.properties
new file mode 100644
index 0000000000..3563a11978
--- /dev/null
+++ b/log4j-jul/src/test/resources/ResourceBundleTest.properties
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+##
+# Resource bundle used in ResourceBundleTest
+
+msg_1 = Hello!
+msg_2 = Hello %s and %s!
diff --git a/log4j-jul/src/test/resources/log4j2-calling-class.xml b/log4j-jul/src/test/resources/ResourceBundleTest.xml
similarity index 68%
rename from log4j-jul/src/test/resources/log4j2-calling-class.xml
rename to log4j-jul/src/test/resources/ResourceBundleTest.xml
index 1af22eaa82..3d2d891500 100644
--- a/log4j-jul/src/test/resources/log4j2-calling-class.xml
+++ b/log4j-jul/src/test/resources/ResourceBundleTest.xml
@@ -15,22 +15,17 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<Configuration name="CallerInformationTest" status="ERROR">
+<Configuration xmlns="https://logging.apache.org/xml/ns"
+               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+               xsi:schemaLocation="
+                   https://logging.apache.org/xml/ns
+                   https://logging.apache.org/xml/ns/log4j-config-3.xsd">
   <Appenders>
-    <List name="Class">
-      <PatternLayout pattern="%class"/>
-    </List>
-    <List name="Method">
-      <PatternLayout pattern="%method"/>
-    </List>
+    <List name="LIST"/>
   </Appenders>
   <Loggers>
-    <Logger name="ClassLogger" level="info">
-      <AppenderRef ref="Class"/>
-    </Logger>
-    <Logger name="MethodLogger" level="info">
-      <AppenderRef ref="Method"/>
-    </Logger>
-    <Root level="off"/>
+    <Root level="INFO">
+      <AppenderRef ref="LIST"/>
+    </Root>
   </Loggers>
 </Configuration>