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.
