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

pkarwasz pushed a commit to branch fix/configuration-status-attribute
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 2fd0d0c37b078047cb9240e9be607bde80f24699
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Wed Feb 14 13:57:04 2024 +0100

    Register a single reference counted StatusConsoleListener
    
    This adds a single `StatusConsoleListener` that is registered by the
    first Log4j Core configuration active and removed by the last one.
---
 .../log4j/config/PropertiesConfiguration.java      |  4 +-
 .../org/apache/log4j/xml/XmlConfiguration.java     |  4 +-
 .../log4j/status/StatusConsoleListener.java        | 26 ++-------
 .../apache/logging/log4j/status/StatusLogger.java  |  3 +-
 .../log4j/core/config/AbstractConfiguration.java   |  9 +++
 .../config/builder/impl/BuiltConfiguration.java    |  5 +-
 .../config/composite/CompositeConfiguration.java   |  2 +-
 .../log4j/core/config/json/JsonConfiguration.java  |  2 +-
 .../core/config/status/StatusConfiguration.java    | 64 ++++++++++++++++++++--
 .../log4j/core/config/xml/XmlConfiguration.java    |  2 +-
 src/site/_release-notes/_2.x.x.adoc                |  1 +
 11 files changed, 81 insertions(+), 41 deletions(-)

diff --git 
a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
 
b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
index 3fc3d88ffe..2a30a83079 100644
--- 
a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
+++ 
b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -45,7 +45,6 @@ import 
org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
 import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.apache.logging.log4j.core.config.status.StatusConfiguration;
 import org.apache.logging.log4j.core.filter.ThresholdFilter;
 import org.apache.logging.log4j.util.LoaderUtil;
 
@@ -313,8 +312,7 @@ public class PropertiesConfiguration extends 
Log4j1Configuration {
             status = OptionConverter.toBoolean(value, false) ? "debug" : 
"error";
         }
 
-        final StatusConfiguration statusConfig = new 
StatusConfiguration().withStatus(status);
-        statusConfig.initialize();
+        getStatusConfiguration().withStatus(status).initialize();
 
         // if log4j.reset=true then reset hierarchy
         final String reset = properties.getProperty(RESET_KEY);
diff --git 
a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java 
b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
index 0aeaa77675..ab4d53ed9d 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -46,7 +46,6 @@ import 
org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
 import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.apache.logging.log4j.core.config.status.StatusConfiguration;
 import org.apache.logging.log4j.core.filter.ThresholdFilter;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
@@ -745,8 +744,7 @@ public class XmlConfiguration extends Log4j1Configuration {
             status = OptionConverter.toBoolean(confDebug, true) ? "debug" : 
"error";
         }
 
-        final StatusConfiguration statusConfig = new 
StatusConfiguration().withStatus(status);
-        statusConfig.initialize();
+        getStatusConfiguration().withStatus(status).initialize();
 
         final String threshold = subst(element.getAttribute(THRESHOLD_ATTR));
         if (threshold != null) {
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java
index 99bcd8d1a7..15babbfb42 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusConsoleListener.java
@@ -34,10 +34,6 @@ public class StatusConsoleListener implements StatusListener 
{
 
     private final Lock lock = new ReentrantLock();
 
-    private final Level initialLevel;
-
-    private final PrintStream initialStream;
-
     // `volatile` is necessary to correctly read the `level` without holding 
the lock
     private volatile Level level;
 
@@ -65,8 +61,8 @@ public class StatusConsoleListener implements StatusListener {
      * @throws NullPointerException on null {@code level} or {@code stream}
      */
     public StatusConsoleListener(final Level level, final PrintStream stream) {
-        this.initialLevel = this.level = requireNonNull(level, "level");
-        this.initialStream = this.stream = requireNonNull(stream, "stream");
+        this.level = requireNonNull(level, "level");
+        this.stream = requireNonNull(stream, "stream");
     }
 
     /**
@@ -149,21 +145,9 @@ public class StatusConsoleListener implements 
StatusListener {
     @Deprecated
     public void setFilters(final String... filters) {}
 
-    /**
-     * Resets the level and output stream to its initial values, and closes 
the output stream, if it is a non-system one.
-     */
     @Override
-    public void close() {
-        final OutputStream oldStream;
-        lock.lock();
-        try {
-            oldStream = stream;
-            stream = initialStream;
-            level = initialLevel;
-        } finally {
-            lock.unlock();
-        }
-        closeNonSystemStream(oldStream);
+    public void close() throws IOException {
+        closeNonSystemStream(stream);
     }
 
     private static void closeNonSystemStream(final OutputStream stream) {
@@ -171,7 +155,7 @@ public class StatusConsoleListener implements 
StatusListener {
         if (stream != System.out && stream != System.err) {
             try {
                 stream.close();
-            } catch (IOException error) {
+            } catch (final IOException error) {
                 // We are at the lowest level of the system.
                 // Hence, there is nothing better we can do but dumping the 
failure.
                 error.printStackTrace(System.err);
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java
index 2abc93857d..ecedeba8cb 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/status/StatusLogger.java
@@ -444,7 +444,7 @@ public class StatusLogger extends AbstractLogger {
     }
 
     /**
-     * Clears the event buffer, removes the <em>registered</em> (not the 
fallback one!) listeners, and resets the fallback listener.
+     * Clears the event buffer, removes the <em>registered</em> (not the 
fallback one!) listeners.
      */
     public void reset() {
         listenerWriteLock.lock();
@@ -458,7 +458,6 @@ public class StatusLogger extends AbstractLogger {
         } finally {
             listenerWriteLock.unlock();
         }
-        fallbackListener.close();
         buffer.clear();
     }
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index d95202be2a..959d7c1677 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -55,6 +55,7 @@ import 
org.apache.logging.log4j.core.config.arbiters.SelectArbiter;
 import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder;
 import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
 import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
 import org.apache.logging.log4j.core.filter.AbstractFilterable;
 import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor;
@@ -145,6 +146,7 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
     private AsyncWaitStrategyFactory asyncWaitStrategyFactory;
     private NanoClock nanoClock = new DummyNanoClock();
     private final WeakReference<LoggerContext> loggerContext;
+    private final StatusConfiguration statusConfiguration;
 
     /**
      * Constructor.
@@ -158,6 +160,8 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
         componentMap.put(Configuration.CONTEXT_PROPERTIES, propertyMap);
         pluginManager = new PluginManager(Node.CATEGORY);
         rootNode = new Node();
+        statusConfiguration =
+                new 
StatusConfiguration().withStatus(getDefaultStatus()).withConfiguration(this);
         setState(State.INITIALIZING);
     }
 
@@ -451,6 +455,7 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
         }
         setStopped();
         LOGGER.debug("Stopped {} OK", this);
+        statusConfiguration.stop();
         return true;
     }
 
@@ -1231,4 +1236,8 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
     public void setNanoClock(final NanoClock nanoClock) {
         this.nanoClock = Objects.requireNonNull(nanoClock, "nanoClock");
     }
+
+    protected StatusConfiguration getStatusConfiguration() {
+        return statusConfiguration;
+    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java
index f1ec35e06a..49a34ae997 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java
@@ -38,7 +38,6 @@ import org.apache.logging.log4j.core.util.Patterns;
  * @since 2.4
  */
 public class BuiltConfiguration extends AbstractConfiguration {
-    private final StatusConfiguration statusConfig;
     protected Component rootComponent;
     private Component loggersComponent;
     private Component appendersComponent;
@@ -51,7 +50,6 @@ public class BuiltConfiguration extends AbstractConfiguration 
{
     public BuiltConfiguration(
             final LoggerContext loggerContext, final ConfigurationSource 
source, final Component rootComponent) {
         super(loggerContext, source);
-        statusConfig = new 
StatusConfiguration().withStatus(getDefaultStatus());
         for (final Component component : rootComponent.getComponents()) {
             switch (component.getPluginType()) {
                 case "Scripts": {
@@ -131,8 +129,9 @@ public class BuiltConfiguration extends 
AbstractConfiguration {
         super.createAdvertiser(advertiserString, configSource, buffer, 
contentType);
     }
 
+    @Override
     public StatusConfiguration getStatusConfiguration() {
-        return statusConfig;
+        return super.getStatusConfiguration();
     }
 
     public void setPluginPackages(final String packages) {
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
index bf859b1411..25faebc50b 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.java
@@ -76,7 +76,7 @@ public class CompositeConfiguration extends 
AbstractConfiguration implements Rec
         for (final AbstractConfiguration config : configurations) {
             mergeStrategy.mergeRootProperties(rootNode, config);
         }
-        final StatusConfiguration statusConfig = new 
StatusConfiguration().withStatus(getDefaultStatus());
+        final StatusConfiguration statusConfig = getStatusConfiguration();
         for (final Map.Entry<String, String> entry : 
rootNode.getAttributes().entrySet()) {
             final String key = entry.getKey();
             final String value = 
getConfigurationStrSubstitutor().replace(entry.getValue());
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
index b574070a43..2a4d5a7039 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/JsonConfiguration.java
@@ -64,7 +64,7 @@ public class JsonConfiguration extends AbstractConfiguration 
implements Reconfig
                 }
             }
             processAttributes(rootNode, root);
-            final StatusConfiguration statusConfig = new 
StatusConfiguration().withStatus(getDefaultStatus());
+            final StatusConfiguration statusConfig = getStatusConfiguration();
             int monitorIntervalSeconds = 0;
             for (final Map.Entry<String, String> entry :
                     rootNode.getAttributes().entrySet()) {
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java
index 1a1c22835a..f61a6b1a97 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java
@@ -23,22 +23,32 @@ import java.io.FileOutputStream;
 import java.io.PrintStream;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.WeakHashMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.util.FileUtils;
 import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.status.StatusConsoleListener;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
  * Configuration for setting up the {@link StatusLogger} fallback listener.
  */
 public class StatusConfiguration {
 
-    private static final StatusLogger LOGGER = StatusLogger.getLogger();
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final Level DEFAULT_LEVEL = Level.toLevel(
+            
PropertiesUtil.getProperties().getStringProperty(StatusLogger.DEFAULT_STATUS_LISTENER_LEVEL),
 Level.ERROR);
 
-    private final Lock lock = new ReentrantLock();
+    // Guard all accesses to listener and activeConfigurations
+    private static final Lock lock = new ReentrantLock();
+    private static StatusConsoleListener listener = new 
StatusConsoleListener(DEFAULT_LEVEL);
+    private static Map<Configuration, Void> activeConfigurations = new 
WeakHashMap<>();
 
     private volatile boolean initialized;
 
@@ -48,6 +58,9 @@ public class StatusConfiguration {
     @Nullable
     private Level level;
 
+    @Nullable
+    private Configuration configuration;
+
     /**
      * Specifies how verbose the StatusLogger should be.
      * @deprecated This class is not used anymore and only kept for binary 
backward compatibility.
@@ -179,19 +192,38 @@ public class StatusConfiguration {
         return this;
     }
 
+    /**
+     * Sets the logger context associated with this configuration.
+     * @param configuration A logger context.
+     * @return this
+     */
+    public StatusConfiguration withConfiguration(final Configuration 
configuration) {
+        this.configuration = configuration;
+        return this;
+    }
+
     /**
      * Configures and initializes the StatusLogger using the configured 
options in this instance.
      */
     public void initialize() {
         lock.lock();
         try {
-            if (!this.initialized) {
-                final StatusConsoleListener fallbackListener = 
LOGGER.getFallbackListener();
+            if (!initialized) {
+                final boolean registerListener = 
activeConfigurations.isEmpty();
+                if (configuration != null) {
+                    activeConfigurations.put(configuration, null);
+                } else {
+                    LOGGER.error("The required `configuration` parameter is 
missing.");
+                    return;
+                }
                 if (output != null) {
-                    fallbackListener.setStream(output);
+                    listener.setStream(output);
                 }
                 if (level != null) {
-                    fallbackListener.setLevel(level);
+                    listener.setLevel(level);
+                }
+                if (registerListener) {
+                    StatusLogger.getLogger().registerListener(listener);
                 }
                 initialized = true;
             }
@@ -199,4 +231,24 @@ public class StatusConfiguration {
             lock.unlock();
         }
     }
+
+    /**
+     * Stops the associated status logger.
+     */
+    public void stop() {
+        lock.lock();
+        try {
+            if (configuration != null) {
+                activeConfigurations.remove(configuration, null);
+            } else {
+                LOGGER.error("The required `configuration` parameter is 
missing.");
+                return;
+            }
+            if (activeConfigurations.isEmpty()) {
+                StatusLogger.getLogger().removeListener(listener);
+            }
+        } finally {
+            lock.unlock();
+        }
+    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java
index 6f6d2f1233..6230b0f0c0 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java
@@ -106,7 +106,7 @@ public class XmlConfiguration extends AbstractConfiguration 
implements Reconfigu
             }
             rootElement = document.getDocumentElement();
             final Map<String, String> attrs = processAttributes(rootNode, 
rootElement);
-            final StatusConfiguration statusConfig = new 
StatusConfiguration().withStatus(getDefaultStatus());
+            final StatusConfiguration statusConfig = getStatusConfiguration();
             int monitorIntervalSeconds = 0;
             for (final Map.Entry<String, String> entry : attrs.entrySet()) {
                 final String key = entry.getKey();
diff --git a/src/site/_release-notes/_2.x.x.adoc 
b/src/site/_release-notes/_2.x.x.adoc
index 79dc38f44b..b2de7e1564 100644
--- a/src/site/_release-notes/_2.x.x.adoc
+++ b/src/site/_release-notes/_2.x.x.adoc
@@ -45,6 +45,7 @@ This releases contains ...
 * Fix regression in `JdkMapAdapterStringMap` performance. 
(https://github.com/apache/logging-log4j2/issues/2238[2238])
 * Allow deserialization of all arrays of allowed classes. 
(https://issues.apache.org/jira/browse/LOG4J2-3680[LOG4J2-3680])
 * Fix forgotten `threadName` field in `RingBufferLogEvent#clear()` 
(https://github.com/apache/logging-log4j2/issues/2234[2234])
+* Fix `StringBuilder` cache corruption on recursive access
 * Fixed use of `SecurityManager` in `LoaderUtil` where 
`AccessController::doPrivileged` should only be invoked when a 
`SecurityManager` is installed. Some runtimes do not seem to have this method 
available. (https://github.com/apache/logging-log4j2/issues/2129[2129])
 * Fix `log4j-spring-cloud-config-client` dependencies to include only those 
required. (https://github.com/apache/logging-log4j2/pull/2157[2157])
 * Fix typo in Kubernetes `clientKeyData` configuration property.

Reply via email to