This is an automated email from the ASF dual-hosted git repository.
vy pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/2.x by this push:
new 724281cec4 Implement `MessageFactory`-namespaced logger registry
(#2936)
724281cec4 is described below
commit 724281cec45300690b42ef863790e5ce1a3d0e57
Author: Volkan Yazıcı <[email protected]>
AuthorDate: Mon Sep 16 13:15:33 2024 +0200
Implement `MessageFactory`-namespaced logger registry (#2936)
Co-authored-by: Piotr P. Karwasz <[email protected]>
---
.../log4j/message/LocalizedMessageFactory.java | 22 ++
.../apache/logging/log4j/message/package-info.java | 2 +-
.../logging/log4j/simple/SimpleLoggerContext.java | 33 +--
.../apache/logging/log4j/simple/package-info.java | 2 +-
.../apache/logging/log4j/spi/AbstractLogger.java | 4 +
.../apache/logging/log4j/spi/LoggerContext.java | 7 +-
.../apache/logging/log4j/spi/LoggerRegistry.java | 294 ++++++++++++++++-----
.../org/apache/logging/log4j/spi/package-info.java | 2 +-
.../java/org/apache/logging/log4j/core/Logger.java | 2 +-
.../apache/logging/log4j/core/LoggerContext.java | 58 ++--
.../logging/log4j/core/async/package-info.java | 2 +-
.../apache/logging/log4j/core/package-info.java | 2 +-
log4j-taglib/pom.xml | 14 +-
.../log4j/taglib/Log4jTaglibLoggerContext.java | 92 ++++---
.../apache/logging/log4j/taglib/package-info.java | 2 +-
.../logging/log4j/tojul/JULLoggerContext.java | 33 ++-
.../apache/logging/log4j/tojul/package-info.java | 2 +-
.../apache/logging/slf4j/SLF4JLoggerContext.java | 34 ++-
.../org/apache/logging/slf4j/package-info.java | 2 +-
pom.xml | 17 ++
...eprecate_AbstractLogger_checkMessageFactory.xml | 8 +
...ke_LoggerRegistry_MessageFactory_namespaced.xml | 9 +
22 files changed, 463 insertions(+), 180 deletions(-)
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java
b/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java
index d8360dff0e..10d75fc907 100644
---
a/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java
+++
b/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java
@@ -16,7 +16,9 @@
*/
package org.apache.logging.log4j.message;
+import java.util.Objects;
import java.util.ResourceBundle;
+import org.jspecify.annotations.Nullable;
/**
* Creates {@link FormattedMessage} instances for {@link MessageFactory2}
methods (and {@link MessageFactory} by
@@ -33,8 +35,11 @@ import java.util.ResourceBundle;
public class LocalizedMessageFactory extends AbstractMessageFactory {
private static final long serialVersionUID = -1996295808703146741L;
+ @Nullable
// FIXME: cannot use ResourceBundle name for serialization until Java 8
private final transient ResourceBundle resourceBundle;
+
+ @Nullable
private final String baseName;
public LocalizedMessageFactory(final ResourceBundle resourceBundle) {
@@ -92,4 +97,21 @@ public class LocalizedMessageFactory extends
AbstractMessageFactory {
}
return new LocalizedMessage(resourceBundle, key, params);
}
+
+ @Override
+ public boolean equals(final Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+ final LocalizedMessageFactory that = (LocalizedMessageFactory) object;
+ return Objects.equals(resourceBundle, that.resourceBundle) &&
Objects.equals(baseName, that.baseName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(resourceBundle, baseName);
+ }
}
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/message/package-info.java
b/log4j-api/src/main/java/org/apache/logging/log4j/message/package-info.java
index 816466b467..e2346e8055 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/package-info.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/package-info.java
@@ -19,7 +19,7 @@
* Public Message Types used for Log4j 2. Users may implement their own
Messages.
*/
@Export
-@Version("2.24.0")
+@Version("2.25.0")
package org.apache.logging.log4j.message;
import org.osgi.annotation.bundle.Export;
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java
b/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java
index d1aaf8db0f..e4052e87a4 100644
---
a/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java
+++
b/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java
@@ -20,12 +20,13 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.PrintStream;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
import org.apache.logging.log4j.simple.internal.SimpleProvider;
-import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.spi.LoggerRegistry;
import org.apache.logging.log4j.util.PropertiesUtil;
+import org.jspecify.annotations.Nullable;
/**
* A simple {@link LoggerContext} implementation.
@@ -41,6 +42,8 @@ public class SimpleLoggerContext implements LoggerContext {
/** All system properties used by <code>SimpleLog</code> start with this */
protected static final String SYSTEM_PREFIX =
"org.apache.logging.log4j.simplelog.";
+ private static final MessageFactory DEFAULT_MESSAGE_FACTORY =
ParameterizedMessageFactory.INSTANCE;
+
private final PropertiesUtil props;
/** Include the instance name in the log message? */
@@ -96,14 +99,14 @@ public class SimpleLoggerContext implements LoggerContext {
}
@Override
- public ExtendedLogger getLogger(final String name, final MessageFactory
messageFactory) {
- // Note: This is the only method where we add entries to the
'loggerRegistry' ivar.
- final ExtendedLogger extendedLogger = loggerRegistry.getLogger(name,
messageFactory);
- if (extendedLogger != null) {
- AbstractLogger.checkMessageFactory(extendedLogger, messageFactory);
- return extendedLogger;
- }
- final SimpleLogger simpleLogger = new SimpleLogger(
+ public ExtendedLogger getLogger(final String name, @Nullable final
MessageFactory messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.computeIfAbsent(name, effectiveMessageFactory,
this::createLogger);
+ }
+
+ private ExtendedLogger createLogger(final String name, @Nullable final
MessageFactory messageFactory) {
+ return new SimpleLogger(
name,
defaultLevel,
showLogName,
@@ -114,8 +117,6 @@ public class SimpleLoggerContext implements LoggerContext {
messageFactory,
props,
stream);
- loggerRegistry.putIfAbsent(name, messageFactory, simpleLogger);
- return loggerRegistry.getLogger(name, messageFactory);
}
/**
@@ -131,16 +132,18 @@ public class SimpleLoggerContext implements LoggerContext
{
@Override
public boolean hasLogger(final String name) {
- return false;
+ return loggerRegistry.hasLogger(name, DEFAULT_MESSAGE_FACTORY);
}
@Override
public boolean hasLogger(final String name, final Class<? extends
MessageFactory> messageFactoryClass) {
- return false;
+ return loggerRegistry.hasLogger(name, messageFactoryClass);
}
@Override
- public boolean hasLogger(final String name, final MessageFactory
messageFactory) {
- return false;
+ public boolean hasLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.hasLogger(name, effectiveMessageFactory);
}
}
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java
b/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java
index 1481df9462..6e77cad0a6 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java
@@ -20,7 +20,7 @@
* Providers are able to be loaded at runtime.
*/
@Export
-@Version("2.24.0")
+@Version("2.25.0")
package org.apache.logging.log4j.simple;
import org.osgi.annotation.bundle.Export;
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
index ecc499ac58..06d99f3f15 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
@@ -166,7 +166,11 @@ public abstract class AbstractLogger implements
ExtendedLogger, LocationAwareLog
*
* @param logger The logger to check
* @param messageFactory The message factory to check.
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
+ * Instead, in {@link LoggerContext#getLogger(String, MessageFactory)}
implementations, namespace loggers with message factories.
+ * If your implementation uses {@link LoggerRegistry}, you are already
covered.
*/
+ @Deprecated
public static void checkMessageFactory(final ExtendedLogger logger, final
MessageFactory messageFactory) {
final String name = logger.getName();
final MessageFactory loggerMessageFactory = logger.getMessageFactory();
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java
index a14af967bf..27874bded0 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.spi;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.MessageFactory;
+import org.jspecify.annotations.Nullable;
/**
* Anchor point for logging implementations.
@@ -54,7 +55,7 @@ public interface LoggerContext {
* @return The logger.
* @since 2.14.0
*/
- default ExtendedLogger getLogger(Class<?> cls, MessageFactory
messageFactory) {
+ default ExtendedLogger getLogger(Class<?> cls, @Nullable MessageFactory
messageFactory) {
final String canonicalName = cls.getCanonicalName();
return getLogger(canonicalName != null ? canonicalName :
cls.getName(), messageFactory);
}
@@ -73,7 +74,7 @@ public interface LoggerContext {
* the logger but will log a warning if mismatched.
* @return The logger with the specified name.
*/
- ExtendedLogger getLogger(String name, MessageFactory messageFactory);
+ ExtendedLogger getLogger(String name, @Nullable MessageFactory
messageFactory);
/**
* Gets the LoggerRegistry.
@@ -118,7 +119,7 @@ public interface LoggerContext {
* @return true if the Logger exists, false otherwise.
* @since 2.5
*/
- boolean hasLogger(String name, MessageFactory messageFactory);
+ boolean hasLogger(String name, @Nullable MessageFactory messageFactory);
/**
* Associates an object into the LoggerContext by name for later use.
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerRegistry.java
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerRegistry.java
index e71c0a933d..323055fb40 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerRegistry.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerRegistry.java
@@ -16,28 +16,50 @@
*/
package org.apache.logging.log4j.spi;
+import static java.util.Objects.requireNonNull;
+
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.BiFunction;
import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
/**
- * Convenience class to be used by {@code LoggerContext} implementations.
+ * Convenience class to be used as an {@link ExtendedLogger} registry by
{@code LoggerContext} implementations.
*/
+@NullMarked
public class LoggerRegistry<T extends ExtendedLogger> {
- private static final String DEFAULT_FACTORY_KEY =
AbstractLogger.DEFAULT_MESSAGE_FACTORY_CLASS.getName();
- private final MapFactory<T> factory;
- private final Map<String, Map<String, T>> map;
+
+ private final Map<String, Map<MessageFactory, WeakReference<T>>>
loggerRefByMessageFactoryByName = new HashMap<>();
+
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ private final Lock readLock = lock.readLock();
+
+ private final Lock writeLock = lock.writeLock();
/**
- * Interface to control the data structure used by the registry to store
the Loggers.
+ * Data structure contract for the internal storage of admitted loggers.
+ *
* @param <T> subtype of {@code ExtendedLogger}
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
*/
+ @Deprecated
public interface MapFactory<T extends ExtendedLogger> {
+
Map<String, T> createInnerMap();
Map<String, Map<String, T>> createOuterMap();
@@ -46,10 +68,14 @@ public class LoggerRegistry<T extends ExtendedLogger> {
}
/**
- * Generates ConcurrentHashMaps for use by the registry to store the
Loggers.
+ * {@link MapFactory} implementation using {@link ConcurrentHashMap}.
+ *
* @param <T> subtype of {@code ExtendedLogger}
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
*/
+ @Deprecated
public static class ConcurrentMapFactory<T extends ExtendedLogger>
implements MapFactory<T> {
+
@Override
public Map<String, T> createInnerMap() {
return new ConcurrentHashMap<>();
@@ -62,15 +88,19 @@ public class LoggerRegistry<T extends ExtendedLogger> {
@Override
public void putIfAbsent(final Map<String, T> innerMap, final String
name, final T logger) {
- ((ConcurrentMap<String, T>) innerMap).putIfAbsent(name, logger);
+ innerMap.putIfAbsent(name, logger);
}
}
/**
- * Generates WeakHashMaps for use by the registry to store the Loggers.
+ * {@link MapFactory} implementation using {@link WeakHashMap}.
+ *
* @param <T> subtype of {@code ExtendedLogger}
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
*/
+ @Deprecated
public static class WeakMapFactory<T extends ExtendedLogger> implements
MapFactory<T> {
+
@Override
public Map<String, T> createInnerMap() {
return new WeakHashMap<>();
@@ -87,43 +117,68 @@ public class LoggerRegistry<T extends ExtendedLogger> {
}
}
- public LoggerRegistry() {
- this(new ConcurrentMapFactory<T>());
- }
+ public LoggerRegistry() {}
- public LoggerRegistry(final MapFactory<T> factory) {
- this.factory = Objects.requireNonNull(factory, "factory");
- this.map = factory.createOuterMap();
- }
-
- private static String factoryClassKey(final Class<? extends
MessageFactory> messageFactoryClass) {
- return messageFactoryClass == null ? DEFAULT_FACTORY_KEY :
messageFactoryClass.getName();
- }
-
- private static String factoryKey(final MessageFactory messageFactory) {
- return messageFactory == null
- ? DEFAULT_FACTORY_KEY
- : messageFactory.getClass().getName();
+ /**
+ * Constructs an instance <b>ignoring</b> the given the map factory.
+ *
+ * @param mapFactory a map factory
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
+ */
+ @Deprecated
+ public LoggerRegistry(@Nullable final MapFactory<T> mapFactory) {
+ this();
}
/**
- * Returns an ExtendedLogger.
- * @param name The name of the Logger to return.
- * @return The logger with the specified name.
+ * Returns the logger associated with the given name.
+ * <p>
+ * There can be made no assumptions on the message factory of the returned
logger.
+ * Callers are strongly advised to switch to {@link #getLogger(String,
MessageFactory)} and <b>provide a message factory parameter!</b>
+ * </p>
+ *
+ * @param name a logger name
+ * @return the logger associated with the name
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
+ * Use {@link #getLogger(String, MessageFactory)} instead.
*/
+ @Deprecated
public T getLogger(final String name) {
- return getOrCreateInnerMap(DEFAULT_FACTORY_KEY).get(name);
+ requireNonNull(name, "name");
+ return getLogger(name, null);
}
/**
- * Returns an ExtendedLogger.
- * @param name The name of the Logger to return.
- * @param messageFactory The message factory is used only when creating a
logger, subsequent use does not change
- * the logger but will log a warning if mismatched.
- * @return The logger with the specified name.
+ * Returns the logger associated with the given name and message factory.
+ * <p>
+ * In the absence of a message factory, there can be made no assumptions
on the message factory of the returned logger.
+ * This lenient behaviour is only kept for backward compatibility.
+ * Callers are strongly advised to <b>provide a message factory parameter
to the method!</b>
+ * </p>
+ *
+ * @param name a logger name
+ * @param messageFactory a message factory
+ * @return the logger associated with the given name and message factory
*/
- public T getLogger(final String name, final MessageFactory messageFactory)
{
- return getOrCreateInnerMap(factoryKey(messageFactory)).get(name);
+ public T getLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ requireNonNull(name, "name");
+ readLock.lock();
+ try {
+ final Map<MessageFactory, WeakReference<T>>
loggerRefByMessageFactory =
+ loggerRefByMessageFactoryByName.get(name);
+ if (loggerRefByMessageFactory == null) {
+ return null;
+ }
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
ParameterizedMessageFactory.INSTANCE;
+ final WeakReference<T> loggerRef =
loggerRefByMessageFactory.get(effectiveMessageFactory);
+ if (loggerRef == null) {
+ return null;
+ }
+ return loggerRef.get();
+ } finally {
+ readLock.unlock();
+ }
}
public Collection<T> getLoggers() {
@@ -131,53 +186,164 @@ public class LoggerRegistry<T extends ExtendedLogger> {
}
public Collection<T> getLoggers(final Collection<T> destination) {
- for (final Map<String, T> inner : map.values()) {
- destination.addAll(inner.values());
+ requireNonNull(destination, "destination");
+ readLock.lock();
+ try {
+ loggerRefByMessageFactoryByName.values().stream()
+ .flatMap(loggerRefByMessageFactory ->
+
loggerRefByMessageFactory.values().stream().map(WeakReference::get))
+ .filter(Objects::nonNull)
+ .forEach(destination::add);
+ } finally {
+ readLock.unlock();
}
return destination;
}
- private Map<String, T> getOrCreateInnerMap(final String factoryName) {
- Map<String, T> inner = map.get(factoryName);
- if (inner == null) {
- inner = factory.createInnerMap();
- map.put(factoryName, inner);
- }
- return inner;
- }
-
/**
- * Detects if a Logger with the specified name exists.
- * @param name The Logger name to search for.
- * @return true if the Logger exists, false otherwise.
+ * Checks if a logger associated with the given name exists.
+ * <p>
+ * There can be made no assumptions on the message factory of the found
logger.
+ * Callers are strongly advised to switch to {@link #hasLogger(String,
MessageFactory)} and <b>provide a message factory parameter!</b>
+ * </p>
+ *
+ * @param name a logger name
+ * @return {@code true}, if the logger exists; {@code false} otherwise.
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
+ * Use {@link #hasLogger(String, MessageFactory)} instead.
*/
+ @Deprecated
public boolean hasLogger(final String name) {
- return getOrCreateInnerMap(DEFAULT_FACTORY_KEY).containsKey(name);
+ requireNonNull(name, "name");
+ final T logger = getLogger(name);
+ return logger != null;
}
/**
- * Detects if a Logger with the specified name and MessageFactory exists.
- * @param name The Logger name to search for.
- * @param messageFactory The message factory to search for.
- * @return true if the Logger exists, false otherwise.
+ * Checks if a logger associated with the given name and message factory
exists.
+ * <p>
+ * In the absence of a message factory, there can be made no assumptions
on the message factory of the found logger.
+ * This lenient behaviour is only kept for backward compatibility.
+ * Callers are strongly advised to <b>provide a message factory parameter
to the method!</b>
+ * </p>
+ *
+ * @param name a logger name
+ * @param messageFactory a message factory
+ * @return {@code true}, if the logger exists; {@code false} otherwise.
* @since 2.5
*/
- public boolean hasLogger(final String name, final MessageFactory
messageFactory) {
- return
getOrCreateInnerMap(factoryKey(messageFactory)).containsKey(name);
+ public boolean hasLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ requireNonNull(name, "name");
+ final T logger = getLogger(name, messageFactory);
+ return logger != null;
}
/**
- * Detects if a Logger with the specified name and MessageFactory type
exists.
- * @param name The Logger name to search for.
- * @param messageFactoryClass The message factory class to search for.
- * @return true if the Logger exists, false otherwise.
+ * Checks if a logger associated with the given name and message factory
type exists.
+ *
+ * @param name a logger name
+ * @param messageFactoryClass a message factory class
+ * @return {@code true}, if the logger exists; {@code false} otherwise.
* @since 2.5
*/
public boolean hasLogger(final String name, final Class<? extends
MessageFactory> messageFactoryClass) {
- return
getOrCreateInnerMap(factoryClassKey(messageFactoryClass)).containsKey(name);
+ requireNonNull(name, "name");
+ requireNonNull(messageFactoryClass, "messageFactoryClass");
+ readLock.lock();
+ try {
+ return loggerRefByMessageFactoryByName.getOrDefault(name,
Collections.emptyMap()).keySet().stream()
+ .anyMatch(messageFactory ->
messageFactoryClass.equals(messageFactory.getClass()));
+ } finally {
+ readLock.unlock();
+ }
+ }
+
+ /**
+ * Registers the provided logger using the given name – <b>message factory
parameter is ignored</b> and the one from the logger will be used instead.
+ *
+ * @param name a logger name
+ * @param messageFactory ignored – kept for backward compatibility
+ * @param logger a logger instance
+ * @deprecated As of version {@code 2.25.0}, planned to be removed!
+ * Use {@link #computeIfAbsent(String, MessageFactory, BiFunction)}
instead.
+ */
+ @Deprecated
+ public void putIfAbsent(final String name, @Nullable final MessageFactory
messageFactory, final T logger) {
+
+ // Check arguments
+ requireNonNull(name, "name");
+ requireNonNull(logger, "logger");
+
+ // Insert the logger
+ writeLock.lock();
+ try {
+ final Map<MessageFactory, WeakReference<T>>
loggerRefByMessageFactory =
+ loggerRefByMessageFactoryByName.computeIfAbsent(name,
this::createLoggerRefByMessageFactoryMap);
+ final MessageFactory loggerMessageFactory =
logger.getMessageFactory();
+ final WeakReference<T> loggerRef =
loggerRefByMessageFactory.get(loggerMessageFactory);
+ if (loggerRef == null || loggerRef.get() == null) {
+ loggerRefByMessageFactory.put(loggerMessageFactory, new
WeakReference<>(logger));
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ public T computeIfAbsent(
+ final String name,
+ final MessageFactory messageFactory,
+ final BiFunction<String, MessageFactory, T> loggerSupplier) {
+
+ // Check arguments
+ requireNonNull(name, "name");
+ requireNonNull(messageFactory, "messageFactory");
+ requireNonNull(loggerSupplier, "loggerSupplier");
+
+ // Read lock fast path: See if logger already exists
+ T logger = getLogger(name, messageFactory);
+ if (logger != null) {
+ return logger;
+ }
+
+ // Write lock slow path: Insert the logger
+ writeLock.lock();
+ try {
+
+ // See if the logger is created by another thread in the meantime
+ final Map<MessageFactory, WeakReference<T>>
loggerRefByMessageFactory =
+ loggerRefByMessageFactoryByName.computeIfAbsent(name,
this::createLoggerRefByMessageFactoryMap);
+ final WeakReference<T> loggerRef;
+ if ((loggerRef = loggerRefByMessageFactory.get(messageFactory)) !=
null
+ && (logger = loggerRef.get()) != null) {
+ return logger;
+ }
+
+ // Create the logger
+ logger = loggerSupplier.apply(name, messageFactory);
+
+ // Report message factory mismatches, if there is any
+ final MessageFactory loggerMessageFactory =
logger.getMessageFactory();
+ if (!loggerMessageFactory.equals(messageFactory)) {
+ StatusLogger.getLogger()
+ .error(
+ "Newly registered logger with name `{}` and
message factory `{}`, is requested to be associated with a different message
factory: `{}`.\n"
+ + "Effectively the message factory of
the logger will be used and the other one will be ignored.\n"
+ + "This generally hints a problem at
the logger context implementation.\n"
+ + "Please report this using the Log4j
project issue tracker.",
+ name,
+ loggerMessageFactory,
+ messageFactory);
+ }
+
+ // Insert the logger
+ loggerRefByMessageFactory.put(loggerMessageFactory, new
WeakReference<>(logger));
+ return logger;
+ } finally {
+ writeLock.unlock();
+ }
}
- public void putIfAbsent(final String name, final MessageFactory
messageFactory, final T logger) {
- factory.putIfAbsent(getOrCreateInnerMap(factoryKey(messageFactory)),
name, logger);
+ private Map<MessageFactory, WeakReference<T>>
createLoggerRefByMessageFactoryMap(final String ignored) {
+ return new WeakHashMap<>();
}
}
diff --git
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/package-info.java
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/package-info.java
index 8fff7b8097..3b6b5c2585 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/package-info.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/package-info.java
@@ -19,7 +19,7 @@
* API classes.
*/
@Export
-@Version("2.24.0")
+@Version("2.25.0")
package org.apache.logging.log4j.spi;
import org.osgi.annotation.bundle.Export;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
index 777d627115..169c39162d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java
@@ -111,7 +111,7 @@ public class Logger extends AbstractLogger implements
Supplier<LoggerConfig> {
this.privateConfig = new PrivateConfig(context.getConfiguration(),
this);
}
- private static MessageFactory getEffectiveMessageFactory(final
MessageFactory messageFactory) {
+ static MessageFactory getEffectiveMessageFactory(final MessageFactory
messageFactory) {
return createInstanceFromFactoryProperty(
MessageFactory.class,
messageFactory,
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index 07cd2d1f59..1e6c3a47fe 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -48,14 +48,15 @@ import org.apache.logging.log4j.core.util.ExecutorServices;
import org.apache.logging.log4j.core.util.NetUtils;
import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.message.MessageFactory;
-import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.LoggerContextFactory;
import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
import org.apache.logging.log4j.spi.LoggerContextShutdownEnabled;
import org.apache.logging.log4j.spi.LoggerRegistry;
import org.apache.logging.log4j.spi.Terminable;
import org.apache.logging.log4j.spi.ThreadContextMapFactory;
+import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.PropertiesUtil;
+import org.jspecify.annotations.Nullable;
/**
* The LoggerContext is the anchor for the logging system. It maintains a list
of all the loggers requested by
@@ -76,6 +77,13 @@ public class LoggerContext extends AbstractLifeCycle
private static final Configuration NULL_CONFIGURATION = new
NullConfiguration();
+ /**
+ * The default message factory to use while creating loggers, if none is
provided.
+ *
+ * @see <a
href="https://github.com/apache/logging-log4j2/pull/2936">#2936</a> for the
discussion on why we leak the message factory of the default logger and
hardcode it here.
+ */
+ private static final MessageFactory DEFAULT_MESSAGE_FACTORY =
Logger.getEffectiveMessageFactory(null);
+
private final LoggerRegistry<Logger> loggerRegistry = new
LoggerRegistry<>();
private final CopyOnWriteArrayList<PropertyChangeListener>
propertyChangeListeners = new CopyOnWriteArrayList<>();
private volatile List<LoggerContextShutdownAware> listeners;
@@ -515,25 +523,17 @@ public class LoggerContext extends AbstractLifeCycle
}
/**
- * Obtains a Logger from the Context.
+ * Obtains a logger from the context.
*
- * @param name The name of the Logger to return.
- * @param messageFactory The message factory is used only when creating a
logger, subsequent use does not change the
- * logger but will log a warning if mismatched.
- * @return The Logger.
+ * @param name a logger name
+ * @param messageFactory a message factory to associate the logger with
+ * @return a logger matching the given name and message factory
*/
@Override
- public Logger getLogger(final String name, final MessageFactory
messageFactory) {
- // Note: This is the only method where we add entries to the
'loggerRegistry' ivar.
- Logger logger = loggerRegistry.getLogger(name, messageFactory);
- if (logger != null) {
- AbstractLogger.checkMessageFactory(logger, messageFactory);
- return logger;
- }
-
- logger = newInstance(this, name, messageFactory);
- loggerRegistry.putIfAbsent(name, messageFactory, logger);
- return loggerRegistry.getLogger(name, messageFactory);
+ public Logger getLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.computeIfAbsent(name, effectiveMessageFactory,
this::newInstance);
}
/**
@@ -554,7 +554,7 @@ public class LoggerContext extends AbstractLifeCycle
*/
@Override
public boolean hasLogger(final String name) {
- return loggerRegistry.hasLogger(name);
+ return loggerRegistry.hasLogger(name, DEFAULT_MESSAGE_FACTORY);
}
/**
@@ -564,8 +564,10 @@ public class LoggerContext extends AbstractLifeCycle
* @return True if the Logger exists, false otherwise.
*/
@Override
- public boolean hasLogger(final String name, final MessageFactory
messageFactory) {
- return loggerRegistry.hasLogger(name, messageFactory);
+ public boolean hasLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.hasLogger(name, effectiveMessageFactory);
}
/**
@@ -812,6 +814,22 @@ public class LoggerContext extends AbstractLifeCycle
.init(); // Or make public and call ThreadContext.init() which
calls ThreadContextMapFactory.init().
}
+ private Logger newInstance(final String name, final MessageFactory
messageFactory) {
+ final Logger logger = newInstance(this, name, messageFactory);
+ final MessageFactory loggerMessageFactory = logger.getMessageFactory();
+ if (!loggerMessageFactory.equals(messageFactory)) {
+ StatusLogger.getLogger()
+ .error(
+ "Newly created logger with name `{}` and message
factory `{}` was actually requested to be created with a different message
factory: `{}`.\n"
+ + "This generally hints a problem.\n"
+ + "Please report this using the Log4j
project issue tracker.",
+ name,
+ loggerMessageFactory,
+ messageFactory);
+ }
+ return logger;
+ }
+
// LOG4J2-151: changed visibility from private to protected
protected Logger newInstance(final LoggerContext ctx, final String name,
final MessageFactory messageFactory) {
return new Logger(ctx, name, messageFactory);
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/package-info.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/package-info.java
index 92bb0d5449..abf19fcd6f 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/package-info.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/package-info.java
@@ -18,7 +18,7 @@
* Provides Asynchronous Logger classes and interfaces for low-latency logging.
*/
@Export
-@Version("2.24.0")
+@Version("2.25.0")
package org.apache.logging.log4j.core.async;
import org.osgi.annotation.bundle.Export;
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/package-info.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/package-info.java
index 266256b463..171f9c7e78 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/package-info.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/package-info.java
@@ -18,7 +18,7 @@
* Implementation of Log4j 2.
*/
@Export
-@Version("2.24.0")
+@Version("2.25.0")
package org.apache.logging.log4j.core;
import org.osgi.annotation.bundle.Export;
diff --git a/log4j-taglib/pom.xml b/log4j-taglib/pom.xml
index f34353488a..3855d8f441 100644
--- a/log4j-taglib/pom.xml
+++ b/log4j-taglib/pom.xml
@@ -28,14 +28,19 @@
<name>Apache Log4j Tag Library</name>
<description>The Apache Log4j Tag Library for Web Applications</description>
<properties>
-
<!--
~ OSGi and JPMS options
-->
+ <bnd-extra-package-options>
+ <!-- Annotations only -->
+ org.jspecify.*;resolution:=optional
+ </bnd-extra-package-options>
<bnd-extra-module-options>
<!-- Filebased module names: MUST be static -->
javax.servlet.api;substitute="javax.servlet-api";static=true;transitive=false,
-
javax.servlet.jsp.api;substitute="javax.servlet.jsp-api";static=true;transitive=false
+
javax.servlet.jsp.api;substitute="javax.servlet.jsp-api";static=true;transitive=false,
+ <!-- Remove `transitive` for optional dependencies -->
+ org.jspecify;transitive=false
</bnd-extra-module-options>
<Fragment-Host>org.apache.logging.log4j.core</Fragment-Host>
</properties>
@@ -59,6 +64,11 @@
<artifactId>log4j-web</artifactId>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.jspecify</groupId>
+ <artifactId>jspecify</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
diff --git
a/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/Log4jTaglibLoggerContext.java
b/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/Log4jTaglibLoggerContext.java
index 3c76f39b21..8203f9c736 100644
---
a/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/Log4jTaglibLoggerContext.java
+++
b/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/Log4jTaglibLoggerContext.java
@@ -16,14 +16,20 @@
*/
package org.apache.logging.log4j.taglib;
+import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.servlet.ServletContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.message.MessageFactory;
-import org.apache.logging.log4j.spi.AbstractLogger;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.spi.LoggerRegistry;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
/**
* This bridge between the tag library and the Log4j API ensures that
instances of {@link Log4jTaglibLogger} are
@@ -31,13 +37,21 @@ import org.apache.logging.log4j.spi.LoggerRegistry;
*
* @since 2.0
*/
+@NullMarked
final class Log4jTaglibLoggerContext implements LoggerContext {
- // These were change to WeakHashMaps to avoid ClassLoader (memory) leak,
something that's particularly
- // important in Servlet containers.
- private static final WeakHashMap<ServletContext, Log4jTaglibLoggerContext>
CONTEXTS = new WeakHashMap<>();
- private final LoggerRegistry<Log4jTaglibLogger> loggerRegistry =
- new LoggerRegistry<>(new
LoggerRegistry.WeakMapFactory<Log4jTaglibLogger>());
+ private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
+
+ private static final Lock READ_LOCK = LOCK.readLock();
+
+ private static final Lock WRITE_LOCK = LOCK.writeLock();
+
+ private static final Map<ServletContext, Log4jTaglibLoggerContext>
LOGGER_CONTEXT_BY_SERVLET_CONTEXT =
+ new WeakHashMap<>();
+
+ private static final MessageFactory DEFAULT_MESSAGE_FACTORY =
ParameterizedMessageFactory.INSTANCE;
+
+ private final LoggerRegistry<Log4jTaglibLogger> loggerRegistry = new
LoggerRegistry<>();
private final ServletContext servletContext;
@@ -47,36 +61,25 @@ final class Log4jTaglibLoggerContext implements
LoggerContext {
@Override
public Object getExternalContext() {
- return this.servletContext;
+ return servletContext;
}
@Override
public Log4jTaglibLogger getLogger(final String name) {
- return this.getLogger(name, null);
+ return getLogger(name, null);
}
@Override
- public Log4jTaglibLogger getLogger(final String name, final MessageFactory
messageFactory) {
- // Note: This is the only method where we add entries to the
'loggerRegistry' ivar.
- Log4jTaglibLogger logger = this.loggerRegistry.getLogger(name,
messageFactory);
- if (logger != null) {
- AbstractLogger.checkMessageFactory(logger, messageFactory);
- return logger;
- }
-
- synchronized (this.loggerRegistry) {
- logger = this.loggerRegistry.getLogger(name, messageFactory);
- if (logger == null) {
- final LoggerContext context = LogManager.getContext(false);
- final ExtendedLogger original =
- messageFactory == null ? context.getLogger(name) :
context.getLogger(name, messageFactory);
- // wrap a logger from an underlying implementation
- logger = new Log4jTaglibLogger(original, name,
original.getMessageFactory());
- this.loggerRegistry.putIfAbsent(name, messageFactory, logger);
- }
- }
+ public Log4jTaglibLogger getLogger(final String name, @Nullable final
MessageFactory messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.computeIfAbsent(name, effectiveMessageFactory,
this::createLogger);
+ }
- return logger;
+ private Log4jTaglibLogger createLogger(final String name, @Nullable final
MessageFactory messageFactory) {
+ final LoggerContext loggerContext = LogManager.getContext(false);
+ final ExtendedLogger delegateLogger = loggerContext.getLogger(name,
messageFactory);
+ return new Log4jTaglibLogger(delegateLogger, name,
delegateLogger.getMessageFactory());
}
@Override
@@ -85,8 +88,10 @@ final class Log4jTaglibLoggerContext implements
LoggerContext {
}
@Override
- public boolean hasLogger(final String name, final MessageFactory
messageFactory) {
- return loggerRegistry.hasLogger(name, messageFactory);
+ public boolean hasLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.hasLogger(name, effectiveMessageFactory);
}
@Override
@@ -94,20 +99,25 @@ final class Log4jTaglibLoggerContext implements
LoggerContext {
return loggerRegistry.hasLogger(name, messageFactoryClass);
}
- static synchronized Log4jTaglibLoggerContext getInstance(final
ServletContext servletContext) {
- Log4jTaglibLoggerContext loggerContext = CONTEXTS.get(servletContext);
- if (loggerContext != null) {
- return loggerContext;
- }
+ static Log4jTaglibLoggerContext getInstance(final ServletContext
servletContext) {
- synchronized (CONTEXTS) {
- loggerContext = CONTEXTS.get(servletContext);
- if (loggerContext == null) {
- loggerContext = new Log4jTaglibLoggerContext(servletContext);
- CONTEXTS.put(servletContext, loggerContext);
+ // Get the associated logger context, if exists
+ READ_LOCK.lock();
+ try {
+ final Log4jTaglibLoggerContext loggerContext =
LOGGER_CONTEXT_BY_SERVLET_CONTEXT.get(servletContext);
+ if (loggerContext != null) {
+ return loggerContext;
}
+ } finally {
+ READ_LOCK.unlock();
}
- return loggerContext;
+ // Create the logger context
+ WRITE_LOCK.lock();
+ try {
+ return
LOGGER_CONTEXT_BY_SERVLET_CONTEXT.computeIfAbsent(servletContext,
Log4jTaglibLoggerContext::new);
+ } finally {
+ WRITE_LOCK.unlock();
+ }
}
}
diff --git
a/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/package-info.java
b/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/package-info.java
index 3614d93117..9f8d273e7d 100644
---
a/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/package-info.java
+++
b/log4j-taglib/src/main/java/org/apache/logging/log4j/taglib/package-info.java
@@ -20,7 +20,7 @@
* @since 2.0
*/
@Export
-@Version("2.20.1")
+@Version("2.25.0")
package org.apache.logging.log4j.taglib;
import org.osgi.annotation.bundle.Export;
diff --git
a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLoggerContext.java
b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLoggerContext.java
index 7d2c2351c6..3cca8318b8 100644
---
a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLoggerContext.java
+++
b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLoggerContext.java
@@ -18,9 +18,11 @@ package org.apache.logging.log4j.tojul;
import java.util.logging.Logger;
import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.spi.LoggerRegistry;
+import org.jspecify.annotations.Nullable;
/**
* Implementation of Log4j {@link LoggerContext} SPI.
@@ -29,8 +31,11 @@ import org.apache.logging.log4j.spi.LoggerRegistry;
* @author <a href="http://www.vorburger.ch">Michael Vorburger.ch</a> for
Google
*/
class JULLoggerContext implements LoggerContext {
+
private final LoggerRegistry<ExtendedLogger> loggerRegistry = new
LoggerRegistry<>();
+ private static final MessageFactory DEFAULT_MESSAGE_FACTORY =
ParameterizedMessageFactory.INSTANCE;
+
// This implementation is strongly inspired by
org.apache.logging.slf4j.SLF4JLoggerContext
@Override
@@ -40,29 +45,31 @@ class JULLoggerContext implements LoggerContext {
@Override
public ExtendedLogger getLogger(final String name) {
- if (!loggerRegistry.hasLogger(name)) {
- loggerRegistry.putIfAbsent(name, null, new JULLogger(name,
Logger.getLogger(name)));
- }
- return loggerRegistry.getLogger(name);
+ return getLogger(name, null);
}
@Override
- public ExtendedLogger getLogger(final String name, final MessageFactory
messageFactory) {
- if (!loggerRegistry.hasLogger(name, messageFactory)) {
- loggerRegistry.putIfAbsent(
- name, messageFactory, new JULLogger(name, messageFactory,
Logger.getLogger(name)));
- }
- return loggerRegistry.getLogger(name, messageFactory);
+ public ExtendedLogger getLogger(final String name, @Nullable final
MessageFactory messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.computeIfAbsent(name, effectiveMessageFactory,
JULLoggerContext::createLogger);
+ }
+
+ private static ExtendedLogger createLogger(final String name, @Nullable
final MessageFactory messageFactory) {
+ final Logger logger = Logger.getLogger(name);
+ return new JULLogger(name, messageFactory, logger);
}
@Override
public boolean hasLogger(final String name) {
- return loggerRegistry.hasLogger(name);
+ return loggerRegistry.hasLogger(name, DEFAULT_MESSAGE_FACTORY);
}
@Override
- public boolean hasLogger(final String name, final MessageFactory
messageFactory) {
- return loggerRegistry.hasLogger(name, messageFactory);
+ public boolean hasLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.hasLogger(name, effectiveMessageFactory);
}
@Override
diff --git
a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java
b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java
index aa117c7995..68812bb248 100644
---
a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java
+++
b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java
@@ -21,7 +21,7 @@
* @author <a href="http://www.vorburger.ch">Michael Vorburger.ch</a> for
Google
*/
@Export
-@Version("2.24.0")
+@Version("2.25.0")
package org.apache.logging.log4j.tojul;
import org.osgi.annotation.bundle.Export;
diff --git
a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLoggerContext.java
b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLoggerContext.java
index f0aee0af41..4e4567d1ef 100644
---
a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLoggerContext.java
+++
b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/SLF4JLoggerContext.java
@@ -17,14 +17,20 @@
package org.apache.logging.slf4j;
import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.spi.LoggerRegistry;
+import org.jspecify.annotations.Nullable;
+import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SLF4JLoggerContext implements LoggerContext {
+
private final LoggerRegistry<ExtendedLogger> loggerRegistry = new
LoggerRegistry<>();
+ private static final MessageFactory DEFAULT_MESSAGE_FACTORY =
ParameterizedMessageFactory.INSTANCE;
+
@Override
public Object getExternalContext() {
return null;
@@ -32,29 +38,31 @@ public class SLF4JLoggerContext implements LoggerContext {
@Override
public ExtendedLogger getLogger(final String name) {
- if (!loggerRegistry.hasLogger(name)) {
- loggerRegistry.putIfAbsent(name, null, new SLF4JLogger(name,
LoggerFactory.getLogger(name)));
- }
- return loggerRegistry.getLogger(name);
+ return getLogger(name, null);
}
@Override
- public ExtendedLogger getLogger(final String name, final MessageFactory
messageFactory) {
- if (!loggerRegistry.hasLogger(name, messageFactory)) {
- loggerRegistry.putIfAbsent(
- name, messageFactory, new SLF4JLogger(name,
messageFactory, LoggerFactory.getLogger(name)));
- }
- return loggerRegistry.getLogger(name, messageFactory);
+ public ExtendedLogger getLogger(final String name, @Nullable final
MessageFactory messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.computeIfAbsent(name, effectiveMessageFactory,
SLF4JLoggerContext::createLogger);
+ }
+
+ private static ExtendedLogger createLogger(final String name, @Nullable
final MessageFactory messageFactory) {
+ final Logger logger = LoggerFactory.getLogger(name);
+ return new SLF4JLogger(name, messageFactory, logger);
}
@Override
public boolean hasLogger(final String name) {
- return loggerRegistry.hasLogger(name);
+ return loggerRegistry.hasLogger(name, DEFAULT_MESSAGE_FACTORY);
}
@Override
- public boolean hasLogger(final String name, final MessageFactory
messageFactory) {
- return loggerRegistry.hasLogger(name, messageFactory);
+ public boolean hasLogger(final String name, @Nullable final MessageFactory
messageFactory) {
+ final MessageFactory effectiveMessageFactory =
+ messageFactory != null ? messageFactory :
DEFAULT_MESSAGE_FACTORY;
+ return loggerRegistry.hasLogger(name, effectiveMessageFactory);
}
@Override
diff --git
a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/package-info.java
b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/package-info.java
index ba4cb130be..8e6a1c3ac8 100644
--- a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/package-info.java
+++ b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/package-info.java
@@ -18,7 +18,7 @@
* SLF4J support.
*/
@Export
-@Version("2.24.0")
+@Version("2.25.0")
package org.apache.logging.slf4j;
import org.osgi.annotation.bundle.Export;
diff --git a/pom.xml b/pom.xml
index 3ac428a3a2..e778a2e640 100644
--- a/pom.xml
+++ b/pom.xml
@@ -963,14 +963,30 @@
<profiles>
+ <!-- `java8-tests` profile to force using Java 8 while running tests -->
<profile>
+
<id>java8-tests</id>
+
<activation>
<property>
<name>env.CI</name>
<value>true</value>
</property>
</activation>
+
+ <!-- There are certain Java 8 bugs[1] that cause Mockito failures[2].
+ Adding necessary dependencies to workaround them.
+ [1] https://bugs.openjdk.org/browse/JDK-8152174
+ [2] https://github.com/mockito/mockito/issues/1449 -->
+ <dependencies>
+ <dependency>
+ <groupId>org.jspecify</groupId>
+ <artifactId>jspecify</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
<build>
<plugins>
<plugin>
@@ -990,6 +1006,7 @@
</plugin>
</plugins>
</build>
+
</profile>
<profile>
diff --git
a/src/changelog/.2.x.x/2936_deprecate_AbstractLogger_checkMessageFactory.xml
b/src/changelog/.2.x.x/2936_deprecate_AbstractLogger_checkMessageFactory.xml
new file mode 100644
index 0000000000..e72a33566a
--- /dev/null
+++ b/src/changelog/.2.x.x/2936_deprecate_AbstractLogger_checkMessageFactory.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="https://logging.apache.org/xml/ns"
+ xsi:schemaLocation="https://logging.apache.org/xml/ns
https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+ type="deprecated">
+ <issue id="2936" link="https://github.com/apache/logging-log4j2/pull/2936"/>
+ <description format="asciidoc">Deprecate
`AbstractLogger.checkMessageFactory()`, since all created `Logger`s are already
`MessageFactory`-namespaced</description>
+</entry>
diff --git
a/src/changelog/.2.x.x/2936_make_LoggerRegistry_MessageFactory_namespaced.xml
b/src/changelog/.2.x.x/2936_make_LoggerRegistry_MessageFactory_namespaced.xml
new file mode 100644
index 0000000000..c314f84251
--- /dev/null
+++
b/src/changelog/.2.x.x/2936_make_LoggerRegistry_MessageFactory_namespaced.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="https://logging.apache.org/xml/ns"
+ xsi:schemaLocation="https://logging.apache.org/xml/ns
https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+ type="changed">
+ <issue id="2936" link="https://github.com/apache/logging-log4j2/pull/2936"/>
+ <description format="asciidoc">Rework `LoggerRegistry` to make it
`MessageFactory`-namespaced.
+This effectively allows loggers of same name, but different message
factory.</description>
+</entry>