From de15d2d84f56e0f494394950fbe60717f26e19fe Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Wed, 19 Feb 2025 11:17:26 +0000 Subject: [PATCH 01/12] Add option to provide URIs to monitor in addition to the config file A change detected in either the config file itself or the provided additional URIs shall result in a reconfigure Signed-off-by: MichaelMorris --- .../apache/logging/log4j/core/LoggerTest.java | 24 +++++++++++++ .../log4j/core/PropertiesFileConfigTest.java | 21 +++++++++++ .../src/test/resources/log4j-test2.properties | 1 + .../src/test/resources/log4j-test2.xml | 2 +- .../core/config/AbstractConfiguration.java | 10 +++++- .../core/config/ConfigurationFileWatcher.java | 35 +++++++++++++++++-- .../builder/api/ConfigurationBuilder.java | 9 +++++ .../core/config/builder/api/package-info.java | 2 +- .../builder/impl/BuiltConfiguration.java | 27 +++++++++++++- .../impl/DefaultConfigurationBuilder.java | 13 ++++++- .../config/builder/impl/package-info.java | 2 +- .../core/config/composite/package-info.java | 2 +- .../core/config/json/JsonConfiguration.java | 11 +++++- .../log4j/core/config/json/package-info.java | 2 +- .../log4j/core/config/package-info.java | 2 +- .../PropertiesConfigurationBuilder.java | 2 ++ .../core/config/properties/package-info.java | 2 +- .../core/config/xml/XmlConfiguration.java | 14 ++++++-- .../log4j/core/config/xml/package-info.java | 2 +- .../log4j/core/config/yaml/package-info.java | 2 +- .../logging/log4j/core/util/WatchManager.java | 21 +++++++++++ .../.2.x.x/3074_monitor_additional_files.xml | 8 +++++ 22 files changed, 197 insertions(+), 17 deletions(-) create mode 100644 src/changelog/.2.x.x/3074_monitor_additional_files.xml diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java index bcd8d7bd26f..4da29b26a1e 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java @@ -544,6 +544,30 @@ void testReconfiguration(final LoggerContext context) throws Exception { assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); } + @Test + void testReconfigurationMonitorUris(final LoggerContext context) throws Exception { + final Configuration oldConfig = context.getConfiguration(); + final int MONITOR_INTERVAL_SECONDS = 5; + final File file = new File("target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12"); + final long orig = file.lastModified(); + final long newTime = orig + 10000; + assertTrue(file.setLastModified(newTime), "setLastModified should have succeeded."); + TimeUnit.SECONDS.sleep(MONITOR_INTERVAL_SECONDS + 1); + for (int i = 0; i < 17; ++i) { + logger.debug("Reconfigure"); + } + Thread.sleep(100); + for (int i = 0; i < 20; i++) { + if (context.getConfiguration() != oldConfig) { + break; + } + Thread.sleep(50); + } + final Configuration newConfig = context.getConfiguration(); + assertNotNull(newConfig, "No configuration"); + assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); + } + @Test void testSuppressedThrowable(final LoggerContext context) { final org.apache.logging.log4j.Logger testLogger = context.getLogger("org.apache.logging.log4j.nothrown"); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java index 4a4bf8dba0a..c10910bb751 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java @@ -64,4 +64,25 @@ void testReconfiguration(final LoggerContext context) throws Exception { } while (newConfig == oldConfig && loopCount++ < 5); assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); } + + @Test + void testReconfigurationMonitorUris(final LoggerContext context) throws Exception { + final Configuration oldConfig = context.getConfiguration(); + final int MONITOR_INTERVAL_SECONDS = 5; + final File file = new File("target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12"); + final long orig = file.lastModified(); + final long newTime = orig + 10000; + assertTrue(file.setLastModified(newTime), "setLastModified should have succeeded."); + TimeUnit.SECONDS.sleep(MONITOR_INTERVAL_SECONDS + 1); + for (int i = 0; i < 17; ++i) { + logger.info("Reconfigure"); + } + int loopCount = 0; + Configuration newConfig; + do { + Thread.sleep(100); + newConfig = context.getConfiguration(); + } while (newConfig == oldConfig && loopCount++ < 5); + assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); + } } diff --git a/log4j-core-test/src/test/resources/log4j-test2.properties b/log4j-core-test/src/test/resources/log4j-test2.properties index d463dd69688..fa264acfe95 100644 --- a/log4j-core-test/src/test/resources/log4j-test2.properties +++ b/log4j-core-test/src/test/resources/log4j-test2.properties @@ -18,6 +18,7 @@ status = debug name = PropertiesConfigTest monitorInterval = 1 +monitorUris = target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12 property.filename = target/test-properties.log diff --git a/log4j-core-test/src/test/resources/log4j-test2.xml b/log4j-core-test/src/test/resources/log4j-test2.xml index 3300d56400b..e01b928183b 100644 --- a/log4j-core-test/src/test/resources/log4j-test2.xml +++ b/log4j-core-test/src/test/resources/log4j-test2.xml @@ -15,7 +15,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 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 5b60a7727b3..7748a709109 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 @@ -275,6 +275,14 @@ protected void initializeWatchers( final Reconfigurable reconfigurable, final ConfigurationSource configSource, final int monitorIntervalSeconds) { + initializeWatchers(reconfigurable, configSource, Collections.emptySet(), monitorIntervalSeconds); + } + + protected void initializeWatchers( + final Reconfigurable reconfigurable, + final ConfigurationSource configSource, + final Collection auxiliarySources, + final int monitorIntervalSeconds) { if (configSource != null && (configSource.getFile() != null || configSource.getURL() != null)) { if (monitorIntervalSeconds > 0) { watchManager.setIntervalSeconds(monitorIntervalSeconds); @@ -284,7 +292,7 @@ protected void initializeWatchers( final long lastModified = file.lastModified(); final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher(this, reconfigurable, listeners, lastModified); - watchManager.watch(cfgSource, watcher); + watchManager.watch(cfgSource, auxiliarySources, watcher); } else if (configSource.getURL() != null) { monitorSource(reconfigurable, configSource); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java index 9b920a45241..90d796f66ba 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java @@ -17,7 +17,10 @@ package org.apache.logging.log4j.core.config; import java.io.File; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.logging.log4j.core.util.AbstractWatcher; import org.apache.logging.log4j.core.util.FileWatcher; import org.apache.logging.log4j.core.util.Source; @@ -30,6 +33,7 @@ public class ConfigurationFileWatcher extends AbstractWatcher implements FileWat private File file; private long lastModifiedMillis; + private Map auxiliaryFiles = new HashMap<>(); public ConfigurationFileWatcher( final Configuration configuration, @@ -42,12 +46,23 @@ public ConfigurationFileWatcher( @Override public long getLastModified() { - return file != null ? file.lastModified() : 0; + Long latestModifiedAuxFile = 0L; + for (final File auxFile : auxiliaryFiles.keySet()) { + if (auxFile.lastModified() > latestModifiedAuxFile) { + latestModifiedAuxFile = auxFile.lastModified(); + } + } + + return file != null + ? file.lastModified() > latestModifiedAuxFile ? file.lastModified() : latestModifiedAuxFile + : 0; } @Override public void fileModified(final File file) { lastModifiedMillis = file.lastModified(); + auxiliaryFiles.entrySet().stream() + .forEach(auxFile -> auxFile.setValue(auxFile.getKey().lastModified())); } @Override @@ -57,9 +72,25 @@ public void watching(final Source source) { super.watching(source); } + /** + * Called when the Watcher is registered. + * @param source the Source that is being watched. + * @param auxiliarySources auxiliary sources also being watched. + */ + public void watching(final Source source, final Collection auxiliarySources) { + file = source.getFile(); + lastModifiedMillis = file.lastModified(); + auxiliarySources.forEach(auxSource -> { + auxiliaryFiles.put(auxSource.getFile(), auxSource.getFile().lastModified()); + }); + super.watching(source); + } + @Override public boolean isModified() { - return lastModifiedMillis != file.lastModified(); + return lastModifiedMillis != file.lastModified() + || auxiliaryFiles.entrySet().stream() + .anyMatch(file -> file.getValue() != file.getKey().lastModified()); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java index 3b9fc489614..7f0115f3cf4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java @@ -418,6 +418,15 @@ public interface ConfigurationBuilder extends Builder setMonitorInterval(String intervalSeconds); + /** + * Set the URIs to be monitored in addition to the configuration file + * @param monitorUris the URIs to monitor + * @return this builder instance + */ + default ConfigurationBuilder setMonitorUris(final String monitorUris) { + return this; + } + /** * Sets the list of packages to search for plugins. * @param packages The comma separated list of packages. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java index 273906a5d48..2f63a9e78c3 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java @@ -20,7 +20,7 @@ * @since 2.4 */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.builder.api; import org.osgi.annotation.bundle.Export; 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 f1ec35e06a0..61a3f0fdc50 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 @@ -18,7 +18,11 @@ import java.io.IOException; import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; @@ -30,6 +34,8 @@ 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.util.Patterns; +import org.apache.logging.log4j.core.util.Source; +import org.apache.logging.log4j.util.Strings; /** * This is the general version of the Configuration created by the Builder. It may be extended to @@ -148,9 +154,28 @@ public void setShutdownTimeoutMillis(final long shutdownTimeoutMillis) { } public void setMonitorInterval(final int intervalSeconds) { + initializeMonitoring(intervalSeconds, ""); + } + + public void initializeMonitoring(final int intervalSeconds, final String monitorUris) { if (this instanceof Reconfigurable && intervalSeconds > 0) { - initializeWatchers((Reconfigurable) this, getConfigurationSource(), intervalSeconds); + initializeWatchers( + (Reconfigurable) this, getConfigurationSource(), getAuxiliarySources(monitorUris), intervalSeconds); + } + } + + private Collection getAuxiliarySources(final String monitorUris) { + final Collection auxiliarySources = new HashSet<>(); + if (Strings.isNotBlank(monitorUris)) { + for (final String uri : Arrays.asList(monitorUris.split(Patterns.COMMA_SEPARATOR))) { + try { + auxiliarySources.add(new Source(new URI(uri))); + } catch (URISyntaxException e) { + LOGGER.error("Error parsing monitorUris: " + monitorUris, e); + } + } } + return auxiliarySources; } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java index 633e619b281..fd77afc49d2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java @@ -61,6 +61,7 @@ import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Throwables; +import org.apache.logging.log4j.util.Strings; /** * @param The BuiltConfiguration type. @@ -80,6 +81,7 @@ public class DefaultConfigurationBuilder implement private final Class clazz; private ConfigurationSource source; private int monitorInterval; + private String monitorUris; private Level level; private String destination; private String packages; @@ -214,7 +216,7 @@ public T build(final boolean initialize) { if (advertiser != null) { configuration.createAdvertiser(advertiser, source); } - configuration.setMonitorInterval(monitorInterval); + configuration.initializeMonitoring(monitorInterval, monitorUris); } catch (final Exception ex) { throw new IllegalArgumentException("Invalid Configuration class specified", ex); } @@ -287,6 +289,9 @@ private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLSt if (monitorInterval > 0) { xmlWriter.writeAttribute("monitorInterval", String.valueOf(monitorInterval)); } + if (Strings.isNotBlank(monitorUris)) { + xmlWriter.writeAttribute("monitorUris", monitorUris); + } writeXmlSection(xmlWriter, properties); writeXmlSection(xmlWriter, scripts); @@ -565,6 +570,12 @@ public ConfigurationBuilder setMonitorInterval(final String intervalSeconds) return this; } + @Override + public ConfigurationBuilder setMonitorUris(final String monitorUris) { + this.monitorUris = monitorUris; + return this; + } + @Override public ConfigurationBuilder setPackages(final String packages) { this.packages = packages; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java index c56f92230f3..4c630135252 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java @@ -20,7 +20,7 @@ * @since 2.4 */ @Export -@Version("2.20.2") +@Version("2.21.0") package org.apache.logging.log4j.core.config.builder.impl; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java index 787cf180434..dd5accfeeea 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java @@ -19,7 +19,7 @@ * Support for composite configurations. */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.composite; import org.osgi.annotation.bundle.Export; 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 b574070a433..6076088c48e 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 @@ -23,8 +23,11 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -39,6 +42,7 @@ import org.apache.logging.log4j.core.config.status.StatusConfiguration; import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Patterns; +import org.apache.logging.log4j.core.util.Source; /** * Creates a Node hierarchy from a JSON file. @@ -66,6 +70,7 @@ public JsonConfiguration(final LoggerContext loggerContext, final ConfigurationS processAttributes(rootNode, root); final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(getDefaultStatus()); int monitorIntervalSeconds = 0; + Collection auxiliarySources = new HashSet<>(); for (final Map.Entry entry : rootNode.getAttributes().entrySet()) { final String key = entry.getKey(); @@ -87,9 +92,13 @@ public JsonConfiguration(final LoggerContext loggerContext, final ConfigurationS monitorIntervalSeconds = Integers.parseInt(value); } else if ("advertiser".equalsIgnoreCase(key)) { createAdvertiser(value, configSource, buffer, "application/json"); + } else if ("monitorUris".equalsIgnoreCase(key)) { + for (final String uri : Arrays.asList(value.split(Patterns.COMMA_SEPARATOR))) { + auxiliarySources.add(new Source(new URI(uri))); + } } } - initializeWatchers(this, configSource, monitorIntervalSeconds); + initializeWatchers(this, configSource, auxiliarySources, monitorIntervalSeconds); statusConfig.initialize(); if (getName() == null) { setName(configSource.getLocation()); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java index 9d9e5d60eb3..5615f2cc73d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with JSON. */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.json; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/package-info.java index 111d1644f68..3db1c7abd6b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/package-info.java @@ -18,7 +18,7 @@ * Configuration of Log4j 2. */ @Export -@Version("2.24.1") +@Version("2.25.0") package org.apache.logging.log4j.core.config; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java index 6dc9fd6957a..594b15dfd4f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java @@ -61,6 +61,7 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory private static final String CONFIG_NAME = "name"; private static final String MONITOR_INTERVAL = "monitorInterval"; private static final String CONFIG_TYPE = "type"; + private static final String MONITOR_URIS = "monitorUris"; private final ConfigurationBuilder builder; private LoggerContext loggerContext; @@ -95,6 +96,7 @@ public PropertiesConfiguration build() { .setPackages(rootProperties.getProperty(PACKAGES)) .setConfigurationName(rootProperties.getProperty(CONFIG_NAME)) .setMonitorInterval(rootProperties.getProperty(MONITOR_INTERVAL, "0")) + .setMonitorUris(rootProperties.getProperty(MONITOR_URIS)) .setAdvertiser(rootProperties.getProperty(ADVERTISER_KEY)); final Properties propertyPlaceholders = PropertiesUtil.extractSubset(rootProperties, "property"); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java index 1563a62e0ec..57419616d65 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java @@ -18,7 +18,7 @@ * Configuration using Properties files. */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.properties; import org.osgi.annotation.bundle.Export; 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 6caeec59ae5..1d86382f8b7 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 @@ -20,9 +20,13 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import javax.xml.XMLConstants; @@ -45,6 +49,7 @@ import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.core.util.Patterns; +import org.apache.logging.log4j.core.util.Source; import org.apache.logging.log4j.core.util.Throwables; import org.w3c.dom.Attr; import org.w3c.dom.Document; @@ -106,6 +111,7 @@ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSo final Map attrs = processAttributes(rootNode, rootElement); final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(getDefaultStatus()); int monitorIntervalSeconds = 0; + Collection auxiliarySources = new HashSet<>(); for (final Map.Entry entry : attrs.entrySet()) { final String key = entry.getKey(); final String value = getConfigurationStrSubstitutor().replace(entry.getValue()); @@ -129,11 +135,15 @@ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSo monitorIntervalSeconds = Integers.parseInt(value); } else if ("advertiser".equalsIgnoreCase(key)) { createAdvertiser(value, configSource, buffer, "text/xml"); + } else if ("monitorUris".equalsIgnoreCase(key)) { + for (final String uri : Arrays.asList(value.split(Patterns.COMMA_SEPARATOR))) { + auxiliarySources.add(new Source(new URI(uri))); + } } } - initializeWatchers(this, configSource, monitorIntervalSeconds); + initializeWatchers(this, configSource, auxiliarySources, monitorIntervalSeconds); statusConfig.initialize(); - } catch (final SAXException | IOException | ParserConfigurationException e) { + } catch (final SAXException | IOException | ParserConfigurationException | URISyntaxException e) { LOGGER.error("Error parsing " + configSource.getLocation(), e); } if (strict && schemaResource != null && buffer != null) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java index f0a6c9d7e4f..79858cbdfd6 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with XML. */ @Export -@Version("2.20.2") +@Version("2.21.0") package org.apache.logging.log4j.core.config.xml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java index c476f1d82c1..ae6af3f58b7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with YAML. */ @Export -@Version("2.20.1") +@Version("2.21.0") package org.apache.logging.log4j.core.config.yaml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java index 3f2856340fd..d3e7133def2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java @@ -21,6 +21,7 @@ import aQute.bnd.annotation.spi.ServiceConsumer; import java.io.File; import java.time.Instant; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -354,6 +355,26 @@ public void watch(final Source source, final Watcher watcher) { watchers.put(source, new ConfigurationMonitor(lastModified, watcher)); } + /** + * Watches the given file. + * + * @param source the source to watch. + * @param auxiliarySources auxiliary sources to also watch + * @param watcher the watcher to notify of file changes. + */ + public void watch( + final Source source, final Collection auxiliarySources, final ConfigurationFileWatcher watcher) { + watcher.watching(source, auxiliarySources); + final long lastModified = watcher.getLastModified(); + if (logger.isDebugEnabled()) { + String message = auxiliarySources.isEmpty() + ? "Watching configuration '{}' for lastModified {} ({})" + : "Watching configuration '{}' for lastModified {} ({}) and files {}"; + logger.debug(message, source, millisToString(lastModified), lastModified); + } + watchers.put(source, new ConfigurationMonitor(lastModified, watcher)); + } + /** * Watches the given file. * diff --git a/src/changelog/.2.x.x/3074_monitor_additional_files.xml b/src/changelog/.2.x.x/3074_monitor_additional_files.xml new file mode 100644 index 00000000000..dcc2d028def --- /dev/null +++ b/src/changelog/.2.x.x/3074_monitor_additional_files.xml @@ -0,0 +1,8 @@ + + + + Support configuration option to provide URIs to monitor in addition to the config file. + From 02d64a30b974b2a80e43802788711e7d8e22ca8b Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Thu, 20 Mar 2025 15:12:46 +0000 Subject: [PATCH 02/12] Reworked to to make monitorUris a dedicated element instead of an attribute of the Configuration element Signed-off-by: MichaelMorris --- .../apache/logging/log4j/core/LoggerTest.java | 24 ----- .../logging/log4j/core/MonitorUriTest.java | 61 +++++++++++++ .../log4j/core/PropertiesFileConfigTest.java | 21 ----- .../src/test/resources/log4j-test2.properties | 1 - .../src/test/resources/log4j-test2.xml | 2 +- .../core/config/AbstractConfiguration.java | 50 ++++++++--- .../core/config/ConfigurationFileWatcher.java | 51 +++++------ .../core/config/ConfigurationSource.java | 4 + .../log4j/core/config/MonitorUris.java | 58 ++++++++++++ .../apache/logging/log4j/core/config/Uri.java | 88 +++++++++++++++++++ .../builder/api/ConfigurationBuilder.java | 27 ++++-- .../api/MonitorUriComponentBuilder.java | 22 +++++ .../core/config/builder/api/package-info.java | 2 +- .../builder/impl/BuiltConfiguration.java | 35 ++------ .../impl/DefaultConfigurationBuilder.java | 29 +++--- .../DefaultMonitorUriComponentBuilder.java | 33 +++++++ .../config/builder/impl/package-info.java | 2 +- .../core/config/composite/package-info.java | 2 +- .../core/config/json/JsonConfiguration.java | 11 +-- .../log4j/core/config/json/package-info.java | 2 +- .../PropertiesConfigurationBuilder.java | 17 +++- .../core/config/properties/package-info.java | 2 +- .../core/config/xml/XmlConfiguration.java | 14 +-- .../log4j/core/config/xml/package-info.java | 2 +- .../log4j/core/config/yaml/package-info.java | 2 +- .../logging/log4j/core/util/WatchManager.java | 35 +++----- 26 files changed, 406 insertions(+), 191 deletions(-) create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorUris.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/Uri.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorUriComponentBuilder.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorUriComponentBuilder.java diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java index 4da29b26a1e..bcd8d7bd26f 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java @@ -544,30 +544,6 @@ void testReconfiguration(final LoggerContext context) throws Exception { assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); } - @Test - void testReconfigurationMonitorUris(final LoggerContext context) throws Exception { - final Configuration oldConfig = context.getConfiguration(); - final int MONITOR_INTERVAL_SECONDS = 5; - final File file = new File("target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12"); - final long orig = file.lastModified(); - final long newTime = orig + 10000; - assertTrue(file.setLastModified(newTime), "setLastModified should have succeeded."); - TimeUnit.SECONDS.sleep(MONITOR_INTERVAL_SECONDS + 1); - for (int i = 0; i < 17; ++i) { - logger.debug("Reconfigure"); - } - Thread.sleep(100); - for (int i = 0; i < 20; i++) { - if (context.getConfiguration() != oldConfig) { - break; - } - Thread.sleep(50); - } - final Configuration newConfig = context.getConfiguration(); - assertNotNull(newConfig, "No configuration"); - assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); - } - @Test void testSuppressedThrowable(final LoggerContext context) { final org.apache.logging.log4j.Logger testLogger = context.getLogger("org.apache.logging.log4j.nothrown"); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java new file mode 100644 index 00000000000..20c519afa55 --- /dev/null +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java @@ -0,0 +1,61 @@ +/* + * 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.core; + +import static org.awaitility.Awaitility.waitAtMost; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.concurrent.TimeUnit; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; +import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration; +import org.apache.logging.log4j.core.util.Source; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.CleanupMode; +import org.junit.jupiter.api.io.TempDir; + +public class MonitorUriTest { + + private static final int MONITOR_INTERVAL = 3; + + @Test + void testReconfigureOnChangeInMonitorUri(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path tempDir) + throws IOException { + ConfigurationBuilder configBuilder = + ConfigurationBuilderFactory.newConfigurationBuilder(PropertiesConfiguration.class); + Path config = tempDir.resolve("config.xml"); + Path monitorUri = tempDir.resolve("monitorUri.xml"); + ConfigurationSource configSource = new ConfigurationSource(new Source(config), new byte[] {}, 0); + Configuration configuration = configBuilder + .setConfigurationSource(configSource) + .setMonitorInterval(String.valueOf(MONITOR_INTERVAL)) + .add(configBuilder.newMonitorUri(monitorUri.toUri().toString())) + .build(); + + try (LoggerContext loggerContext = Configurator.initialize(configuration)) { + Files.write(monitorUri, Collections.singletonList("a change")); + waitAtMost(MONITOR_INTERVAL + 2, TimeUnit.SECONDS) + .until(() -> loggerContext.getConfiguration() != configuration); + } + } +} diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java index c10910bb751..4a4bf8dba0a 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java @@ -64,25 +64,4 @@ void testReconfiguration(final LoggerContext context) throws Exception { } while (newConfig == oldConfig && loopCount++ < 5); assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); } - - @Test - void testReconfigurationMonitorUris(final LoggerContext context) throws Exception { - final Configuration oldConfig = context.getConfiguration(); - final int MONITOR_INTERVAL_SECONDS = 5; - final File file = new File("target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12"); - final long orig = file.lastModified(); - final long newTime = orig + 10000; - assertTrue(file.setLastModified(newTime), "setLastModified should have succeeded."); - TimeUnit.SECONDS.sleep(MONITOR_INTERVAL_SECONDS + 1); - for (int i = 0; i < 17; ++i) { - logger.info("Reconfigure"); - } - int loopCount = 0; - Configuration newConfig; - do { - Thread.sleep(100); - newConfig = context.getConfiguration(); - } while (newConfig == oldConfig && loopCount++ < 5); - assertNotSame(newConfig, oldConfig, "Reconfiguration failed"); - } } diff --git a/log4j-core-test/src/test/resources/log4j-test2.properties b/log4j-core-test/src/test/resources/log4j-test2.properties index fa264acfe95..d463dd69688 100644 --- a/log4j-core-test/src/test/resources/log4j-test2.properties +++ b/log4j-core-test/src/test/resources/log4j-test2.properties @@ -18,7 +18,6 @@ status = debug name = PropertiesConfigTest monitorInterval = 1 -monitorUris = target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12 property.filename = target/test-properties.log diff --git a/log4j-core-test/src/test/resources/log4j-test2.xml b/log4j-core-test/src/test/resources/log4j-test2.xml index e01b928183b..3300d56400b 100644 --- a/log4j-core-test/src/test/resources/log4j-test2.xml +++ b/log4j-core-test/src/test/resources/log4j-test2.xml @@ -15,7 +15,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 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 7748a709109..a53dfe2581e 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 @@ -22,6 +22,7 @@ import java.io.InputStream; import java.lang.ref.WeakReference; import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -132,6 +133,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement private ConcurrentMap appenders = new ConcurrentHashMap<>(); private ConcurrentMap loggerConfigs = new ConcurrentHashMap<>(); private List customLevels = Collections.emptyList(); + private List uris = Collections.emptyList(); private final ConcurrentMap propertyMap = new ConcurrentHashMap<>(); private final Interpolator tempLookup = new Interpolator(propertyMap); private final StrSubstitutor runtimeStrSubstitutor = new RuntimeStrSubstitutor(tempLookup); @@ -275,14 +277,6 @@ protected void initializeWatchers( final Reconfigurable reconfigurable, final ConfigurationSource configSource, final int monitorIntervalSeconds) { - initializeWatchers(reconfigurable, configSource, Collections.emptySet(), monitorIntervalSeconds); - } - - protected void initializeWatchers( - final Reconfigurable reconfigurable, - final ConfigurationSource configSource, - final Collection auxiliarySources, - final int monitorIntervalSeconds) { if (configSource != null && (configSource.getFile() != null || configSource.getURL() != null)) { if (monitorIntervalSeconds > 0) { watchManager.setIntervalSeconds(monitorIntervalSeconds); @@ -292,7 +286,7 @@ protected void initializeWatchers( final long lastModified = file.lastModified(); final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher(this, reconfigurable, listeners, lastModified); - watchManager.watch(cfgSource, auxiliarySources, watcher); + watchManager.watch(cfgSource, watcher); } else if (configSource.getURL() != null) { monitorSource(reconfigurable, configSource); } @@ -331,10 +325,19 @@ public void start() { LOGGER.info("Starting configuration {}...", this); this.setStarting(); if (watchManager.getIntervalSeconds() >= 0) { - LOGGER.info( - "Start watching for changes to {} every {} seconds", - getConfigurationSource(), - watchManager.getIntervalSeconds()); + if (uris != null && uris.size() > 0) { + LOGGER.info( + "Start watching for changes to {} and {} every {} seconds", + getConfigurationSource(), + uris, + watchManager.getIntervalSeconds()); + watchManager.addMonitorUris(configurationSource.getSource(), uris); + } else { + LOGGER.info( + "Start watching for changes to {} every {} seconds", + getConfigurationSource(), + watchManager.getIntervalSeconds()); + } watchManager.start(); } if (hasAsyncLoggers()) { @@ -737,9 +740,16 @@ protected void doConfigure() { } else if (child.isInstanceOf(AsyncWaitStrategyFactoryConfig.class)) { final AsyncWaitStrategyFactoryConfig awsfc = child.getObject(AsyncWaitStrategyFactoryConfig.class); asyncWaitStrategyFactory = awsfc.createWaitStrategyFactory(); + } else if (child.isInstanceOf(MonitorUris.class)) { + uris = convertToJavaNetUris(child.getObject(MonitorUris.class).getUris()); } else { final List expected = Arrays.asList( - "\"Appenders\"", "\"Loggers\"", "\"Properties\"", "\"Scripts\"", "\"CustomLevels\""); + "\"Appenders\"", + "\"Loggers\"", + "\"Properties\"", + "\"Scripts\"", + "\"CustomLevels\"", + "\"MonitorUris\""); LOGGER.error( "Unknown object \"{}\" of type {} is ignored: try nesting it inside one of: {}.", child.getName(), @@ -775,6 +785,18 @@ protected void doConfigure() { setParents(); } + private List convertToJavaNetUris(final List uris) { + final List javaNetUris = new ArrayList<>(); + for (Uri uri : uris) { + try { + javaNetUris.add(new URI(uri.getUri())); + } catch (URISyntaxException e) { + LOGGER.error("Error parsing monitor URI: " + uri, e); + } + } + return javaNetUris; + } + public static Level getDefaultLevel() { final String levelName = PropertiesUtil.getProperties() .getStringProperty(DefaultConfiguration.DEFAULT_LEVEL, Level.ERROR.name()); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java index 90d796f66ba..0dfdb8582fe 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java @@ -17,7 +17,7 @@ package org.apache.logging.log4j.core.config; import java.io.File; -import java.util.Collection; +import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,9 +31,7 @@ */ public class ConfigurationFileWatcher extends AbstractWatcher implements FileWatcher { - private File file; - private long lastModifiedMillis; - private Map auxiliaryFiles = new HashMap<>(); + private Map monitoredFiles = new HashMap<>(); public ConfigurationFileWatcher( final Configuration configuration, @@ -41,56 +39,49 @@ public ConfigurationFileWatcher( final List configurationListeners, long lastModifiedMillis) { super(configuration, reconfigurable, configurationListeners); - this.lastModifiedMillis = lastModifiedMillis; } @Override public long getLastModified() { - Long latestModifiedAuxFile = 0L; - for (final File auxFile : auxiliaryFiles.keySet()) { - if (auxFile.lastModified() > latestModifiedAuxFile) { - latestModifiedAuxFile = auxFile.lastModified(); + Long lastModifiedMillis = 0L; + for (final File monitoredFile : monitoredFiles.keySet()) { + if (monitoredFile.lastModified() > lastModifiedMillis) { + lastModifiedMillis = monitoredFile.lastModified(); } } - - return file != null - ? file.lastModified() > latestModifiedAuxFile ? file.lastModified() : latestModifiedAuxFile - : 0; + return lastModifiedMillis; } @Override public void fileModified(final File file) { - lastModifiedMillis = file.lastModified(); - auxiliaryFiles.entrySet().stream() - .forEach(auxFile -> auxFile.setValue(auxFile.getKey().lastModified())); + monitoredFiles.entrySet().stream() + .forEach(monitoredFile -> + monitoredFile.setValue(monitoredFile.getKey().lastModified())); } @Override public void watching(final Source source) { - file = source.getFile(); - lastModifiedMillis = file.lastModified(); + File file = source.getFile(); + monitoredFiles.put(file, file.lastModified()); super.watching(source); } /** - * Called when the Watcher is registered. - * @param source the Source that is being watched. - * @param auxiliarySources auxiliary sources also being watched. + * Add the given URIs to be watched. + * + * @param monitorUris URIs to also watch */ - public void watching(final Source source, final Collection auxiliarySources) { - file = source.getFile(); - lastModifiedMillis = file.lastModified(); - auxiliarySources.forEach(auxSource -> { - auxiliaryFiles.put(auxSource.getFile(), auxSource.getFile().lastModified()); + public void addMonitorUris(final List monitorUris) { + monitorUris.forEach(uri -> { + File additionalFile = new Source(uri).getFile(); + monitoredFiles.put(additionalFile, additionalFile.lastModified()); }); - super.watching(source); } @Override public boolean isModified() { - return lastModifiedMillis != file.lastModified() - || auxiliaryFiles.entrySet().stream() - .anyMatch(file -> file.getValue() != file.getKey().lastModified()); + return monitoredFiles.entrySet().stream() + .anyMatch(file -> file.getValue() != file.getKey().lastModified()); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java index d6143cdc158..ecef1c831fa 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java @@ -382,4 +382,8 @@ public String toString() { return null; } } + + Source getSource() { + return this.source; + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorUris.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorUris.java new file mode 100644 index 00000000000..d294e8fa083 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorUris.java @@ -0,0 +1,58 @@ +/* + * 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.core.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * Container for MonitorUri objects. + */ +@Plugin(name = "MonitorUris", category = Core.CATEGORY_NAME, printObject = true) +public final class MonitorUris { + + private final List uris; + + private MonitorUris(final Uri[] uris) { + this.uris = new ArrayList<>(Arrays.asList(uris)); + } + + /** + * Create a MonitorUris object to contain all the URIs to be monitored. + * + * @param uris An array of URIs. + * @return A MonitorUris object. + */ + @PluginFactory + public static MonitorUris createMonitorUris( // + @PluginElement("Uris") final Uri[] uris) { + return new MonitorUris(uris == null ? Uri.EMPTY_ARRAY : uris); + } + + /** + * Returns a list of the {@code Uri} objects created during configuration. + * @return the URIs to be monitored + */ + public List getUris() { + return uris; + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Uri.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Uri.java new file mode 100644 index 00000000000..a07dc3e2a03 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Uri.java @@ -0,0 +1,88 @@ +/* + * 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.core.config; + +import java.util.Objects; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.config.plugins.PluginValue; +import org.apache.logging.log4j.status.StatusLogger; + +/** + * Descriptor of a URI object that is created via configuration. + */ +@Plugin(name = "Uri", category = Core.CATEGORY_NAME, printObject = true) +public final class Uri { + + /** + * The empty array. + */ + static final Uri[] EMPTY_ARRAY = {}; + + private final String uri; + + private Uri(final String uri) { + this.uri = Objects.requireNonNull(uri, "uri is null"); + } + + /** + * Creates a Uri object. + * + * @param uri the URI. + * @return A Uri object. + */ + @PluginFactory + public static Uri createUri( // @formatter:off + @PluginValue("uri") final String uri) { + // @formatter:on + + StatusLogger.getLogger().debug("Creating Uri('{}')", uri); + return new Uri(uri); + } + + /** + * Returns the URI. + * + * @return the URI + */ + public String getUri() { + return uri; + } + + @Override + public int hashCode() { + return uri.hashCode(); + } + + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (!(object instanceof Uri)) { + return false; + } + final Uri other = (Uri) object; + return this.uri == other.uri; + } + + @Override + public String toString() { + return "Uri[" + uri + "]"; + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java index 7f0115f3cf4..badbd346cda 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java @@ -61,6 +61,15 @@ public interface ConfigurationBuilder extends Builder add(CustomLevelComponentBuilder builder); + /** + * Adds a MonitorUri component. + * @param builder The MonitorUriComponentBuilder with all of its attributes set. + * @return this builder instance. + */ + default ConfigurationBuilder add(MonitorUriComponentBuilder builder) { + return this; + } + /** * Adds a Filter component. * @param builder the FilterComponentBuilder with all of its attributes and sub components set. @@ -272,6 +281,15 @@ public interface ConfigurationBuilder extends Builder extends Builder setMonitorInterval(String intervalSeconds); - /** - * Set the URIs to be monitored in addition to the configuration file - * @param monitorUris the URIs to monitor - * @return this builder instance - */ - default ConfigurationBuilder setMonitorUris(final String monitorUris) { - return this; - } - /** * Sets the list of packages to search for plugins. * @param packages The comma separated list of packages. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorUriComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorUriComponentBuilder.java new file mode 100644 index 00000000000..4942dc02c37 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorUriComponentBuilder.java @@ -0,0 +1,22 @@ +/* + * 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.core.config.builder.api; + +/** + * Assembler for constructing MonitorUri Components. + */ +public interface MonitorUriComponentBuilder extends ComponentBuilder {} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java index 2f63a9e78c3..6d91c2d89d8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java @@ -20,7 +20,7 @@ * @since 2.4 */ @Export -@Version("2.21.0") +@Version("2.25.0") package org.apache.logging.log4j.core.config.builder.api; import org.osgi.annotation.bundle.Export; 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 61a3f0fdc50..8cf02dde440 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 @@ -18,11 +18,7 @@ import java.io.IOException; import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; @@ -34,8 +30,6 @@ 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.util.Patterns; -import org.apache.logging.log4j.core.util.Source; -import org.apache.logging.log4j.util.Strings; /** * This is the general version of the Configuration created by the Builder. It may be extended to @@ -52,6 +46,7 @@ public class BuiltConfiguration extends AbstractConfiguration { private Component propertiesComponent; private Component customLevelsComponent; private Component scriptsComponent; + private Component monitorUriComponent; private String contentType = "text"; public BuiltConfiguration( @@ -84,6 +79,10 @@ public BuiltConfiguration( customLevelsComponent = component; break; } + case "MonitorUris": { + monitorUriComponent = component; + break; + } } } this.rootComponent = rootComponent; @@ -101,6 +100,9 @@ public void setup() { if (customLevelsComponent.getComponents().size() > 0) { children.add(convertToNode(rootNode, customLevelsComponent)); } + if (monitorUriComponent.getComponents().size() > 0) { + children.add(convertToNode(rootNode, monitorUriComponent)); + } children.add(convertToNode(rootNode, loggersComponent)); children.add(convertToNode(rootNode, appendersComponent)); if (filtersComponent.getComponents().size() > 0) { @@ -154,28 +156,9 @@ public void setShutdownTimeoutMillis(final long shutdownTimeoutMillis) { } public void setMonitorInterval(final int intervalSeconds) { - initializeMonitoring(intervalSeconds, ""); - } - - public void initializeMonitoring(final int intervalSeconds, final String monitorUris) { if (this instanceof Reconfigurable && intervalSeconds > 0) { - initializeWatchers( - (Reconfigurable) this, getConfigurationSource(), getAuxiliarySources(monitorUris), intervalSeconds); - } - } - - private Collection getAuxiliarySources(final String monitorUris) { - final Collection auxiliarySources = new HashSet<>(); - if (Strings.isNotBlank(monitorUris)) { - for (final String uri : Arrays.asList(monitorUris.split(Patterns.COMMA_SEPARATOR))) { - try { - auxiliarySources.add(new Source(new URI(uri))); - } catch (URISyntaxException e) { - LOGGER.error("Error parsing monitorUris: " + monitorUris, e); - } - } + initializeWatchers((Reconfigurable) this, getConfigurationSource(), intervalSeconds); } - return auxiliarySources; } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java index fd77afc49d2..1dc4a66ac1e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java @@ -55,13 +55,13 @@ import org.apache.logging.log4j.core.config.builder.api.KeyValuePairComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.MonitorUriComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.PropertyComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Throwables; -import org.apache.logging.log4j.util.Strings; /** * @param The BuiltConfiguration type. @@ -78,10 +78,10 @@ public class DefaultConfigurationBuilder implement private Component properties; private Component customLevels; private Component scripts; + private Component monitorUris; private final Class clazz; private ConfigurationSource source; private int monitorInterval; - private String monitorUris; private Level level; private String destination; private String packages; @@ -126,6 +126,8 @@ public DefaultConfigurationBuilder(final Class clazz) { components.add(appenders); loggers = new Component("Loggers"); components.add(loggers); + monitorUris = new Component("MonitorUris"); + components.add(monitorUris); } protected ConfigurationBuilder add(final Component parent, final ComponentBuilder builder) { @@ -138,6 +140,11 @@ public ConfigurationBuilder add(final AppenderComponentBuilder builder) { return add(appenders, builder); } + @Override + public ConfigurationBuilder add(final MonitorUriComponentBuilder builder) { + return add(monitorUris, builder); + } + @Override public ConfigurationBuilder add(final CustomLevelComponentBuilder builder) { return add(customLevels, builder); @@ -216,7 +223,7 @@ public T build(final boolean initialize) { if (advertiser != null) { configuration.createAdvertiser(advertiser, source); } - configuration.initializeMonitoring(monitorInterval, monitorUris); + configuration.setMonitorInterval(monitorInterval); } catch (final Exception ex) { throw new IllegalArgumentException("Invalid Configuration class specified", ex); } @@ -289,13 +296,11 @@ private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLSt if (monitorInterval > 0) { xmlWriter.writeAttribute("monitorInterval", String.valueOf(monitorInterval)); } - if (Strings.isNotBlank(monitorUris)) { - xmlWriter.writeAttribute("monitorUris", monitorUris); - } writeXmlSection(xmlWriter, properties); writeXmlSection(xmlWriter, scripts); writeXmlSection(xmlWriter, customLevels); + writeXmlSection(xmlWriter, monitorUris); if (filters.getComponents().size() == 1) { writeXmlComponent(xmlWriter, filters.getComponents().get(0)); } else if (filters.getComponents().size() > 1) { @@ -342,7 +347,6 @@ private void writeXmlAttributes(final XMLStreamWriter xmlWriter, final Component } } - @Override public ScriptComponentBuilder newScript(final String name, final String language, final String text) { return new DefaultScriptComponentBuilder(this, name, language, text); } @@ -458,6 +462,11 @@ public CustomLevelComponentBuilder newCustomLevel(final String name, final int l return new DefaultCustomLevelComponentBuilder(this, name, level); } + @Override + public MonitorUriComponentBuilder newMonitorUri(final String uri) { + return new DefaultMonitorUriComponentBuilder(this, uri); + } + @Override public FilterComponentBuilder newFilter( final String type, final Filter.Result onMatch, final Filter.Result onMismatch) { @@ -570,12 +579,6 @@ public ConfigurationBuilder setMonitorInterval(final String intervalSeconds) return this; } - @Override - public ConfigurationBuilder setMonitorUris(final String monitorUris) { - this.monitorUris = monitorUris; - return this; - } - @Override public ConfigurationBuilder setPackages(final String packages) { this.packages = packages; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorUriComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorUriComponentBuilder.java new file mode 100644 index 00000000000..21299003cf8 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorUriComponentBuilder.java @@ -0,0 +1,33 @@ +/* + * 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.core.config.builder.impl; + +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.builder.api.MonitorUriComponentBuilder; + +/** + * + */ +class DefaultMonitorUriComponentBuilder extends DefaultComponentAndConfigurationBuilder + implements MonitorUriComponentBuilder { + + public DefaultMonitorUriComponentBuilder( + final DefaultConfigurationBuilder builder, final String uri) { + super(builder, "Uri"); + addAttribute("uri", uri); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java index 4c630135252..32a69487d75 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java @@ -20,7 +20,7 @@ * @since 2.4 */ @Export -@Version("2.21.0") +@Version("2.25.0") package org.apache.logging.log4j.core.config.builder.impl; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java index dd5accfeeea..787cf180434 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/composite/package-info.java @@ -19,7 +19,7 @@ * Support for composite configurations. */ @Export -@Version("2.21.0") +@Version("2.20.1") package org.apache.logging.log4j.core.config.composite; import org.osgi.annotation.bundle.Export; 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 6076088c48e..b574070a433 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 @@ -23,11 +23,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.URI; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -42,7 +39,6 @@ import org.apache.logging.log4j.core.config.status.StatusConfiguration; import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Patterns; -import org.apache.logging.log4j.core.util.Source; /** * Creates a Node hierarchy from a JSON file. @@ -70,7 +66,6 @@ public JsonConfiguration(final LoggerContext loggerContext, final ConfigurationS processAttributes(rootNode, root); final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(getDefaultStatus()); int monitorIntervalSeconds = 0; - Collection auxiliarySources = new HashSet<>(); for (final Map.Entry entry : rootNode.getAttributes().entrySet()) { final String key = entry.getKey(); @@ -92,13 +87,9 @@ public JsonConfiguration(final LoggerContext loggerContext, final ConfigurationS monitorIntervalSeconds = Integers.parseInt(value); } else if ("advertiser".equalsIgnoreCase(key)) { createAdvertiser(value, configSource, buffer, "application/json"); - } else if ("monitorUris".equalsIgnoreCase(key)) { - for (final String uri : Arrays.asList(value.split(Patterns.COMMA_SEPARATOR))) { - auxiliarySources.add(new Source(new URI(uri))); - } } } - initializeWatchers(this, configSource, auxiliarySources, monitorIntervalSeconds); + initializeWatchers(this, configSource, monitorIntervalSeconds); statusConfig.initialize(); if (getName() == null) { setName(configSource.getLocation()); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java index 5615f2cc73d..9d9e5d60eb3 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/json/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with JSON. */ @Export -@Version("2.21.0") +@Version("2.20.1") package org.apache.logging.log4j.core.config.json; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java index 594b15dfd4f..8f7cb3e5b1b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java @@ -35,6 +35,7 @@ import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggableComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.MonitorUriComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; @@ -61,7 +62,6 @@ public class PropertiesConfigurationBuilder extends ConfigurationBuilderFactory private static final String CONFIG_NAME = "name"; private static final String MONITOR_INTERVAL = "monitorInterval"; private static final String CONFIG_TYPE = "type"; - private static final String MONITOR_URIS = "monitorUris"; private final ConfigurationBuilder builder; private LoggerContext loggerContext; @@ -96,7 +96,6 @@ public PropertiesConfiguration build() { .setPackages(rootProperties.getProperty(PACKAGES)) .setConfigurationName(rootProperties.getProperty(CONFIG_NAME)) .setMonitorInterval(rootProperties.getProperty(MONITOR_INTERVAL, "0")) - .setMonitorUris(rootProperties.getProperty(MONITOR_URIS)) .setAdvertiser(rootProperties.getProperty(ADVERTISER_KEY)); final Properties propertyPlaceholders = PropertiesUtil.extractSubset(rootProperties, "property"); @@ -126,6 +125,12 @@ public PropertiesConfiguration build() { } } + final Map monitorUris = + PropertiesUtil.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, "monitorUri")); + for (final Map.Entry entry : monitorUris.entrySet()) { + builder.add(createMonitorUri(entry.getKey().trim(), entry.getValue())); + } + final String filterProp = rootProperties.getProperty("filters"); if (filterProp != null) { final String[] filterNames = filterProp.split(","); @@ -252,6 +257,14 @@ private AppenderRefComponentBuilder createAppenderRef(final String key, final Pr return addFiltersToComponent(appenderRefBuilder, properties); } + private MonitorUriComponentBuilder createMonitorUri(final String key, final Properties properties) { + final String uri = (String) properties.remove("uri"); + if (Strings.isEmpty(uri)) { + throw new ConfigurationException("No uri attribute provided for MonitorUri " + key); + } + return builder.newMonitorUri(uri); + } + private LoggerComponentBuilder createLogger(final String key, final Properties properties) { final String levelAndRefs = properties.getProperty(""); final String name = (String) properties.remove(CONFIG_NAME); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java index 57419616d65..1563a62e0ec 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/package-info.java @@ -18,7 +18,7 @@ * Configuration using Properties files. */ @Export -@Version("2.21.0") +@Version("2.20.1") package org.apache.logging.log4j.core.config.properties; import org.osgi.annotation.bundle.Export; 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 1d86382f8b7..6caeec59ae5 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 @@ -20,13 +20,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Map; import javax.xml.XMLConstants; @@ -49,7 +45,6 @@ import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Loader; import org.apache.logging.log4j.core.util.Patterns; -import org.apache.logging.log4j.core.util.Source; import org.apache.logging.log4j.core.util.Throwables; import org.w3c.dom.Attr; import org.w3c.dom.Document; @@ -111,7 +106,6 @@ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSo final Map attrs = processAttributes(rootNode, rootElement); final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(getDefaultStatus()); int monitorIntervalSeconds = 0; - Collection auxiliarySources = new HashSet<>(); for (final Map.Entry entry : attrs.entrySet()) { final String key = entry.getKey(); final String value = getConfigurationStrSubstitutor().replace(entry.getValue()); @@ -135,15 +129,11 @@ public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSo monitorIntervalSeconds = Integers.parseInt(value); } else if ("advertiser".equalsIgnoreCase(key)) { createAdvertiser(value, configSource, buffer, "text/xml"); - } else if ("monitorUris".equalsIgnoreCase(key)) { - for (final String uri : Arrays.asList(value.split(Patterns.COMMA_SEPARATOR))) { - auxiliarySources.add(new Source(new URI(uri))); - } } } - initializeWatchers(this, configSource, auxiliarySources, monitorIntervalSeconds); + initializeWatchers(this, configSource, monitorIntervalSeconds); statusConfig.initialize(); - } catch (final SAXException | IOException | ParserConfigurationException | URISyntaxException e) { + } catch (final SAXException | IOException | ParserConfigurationException e) { LOGGER.error("Error parsing " + configSource.getLocation(), e); } if (strict && schemaResource != null && buffer != null) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java index 79858cbdfd6..f0a6c9d7e4f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with XML. */ @Export -@Version("2.21.0") +@Version("2.20.2") package org.apache.logging.log4j.core.config.xml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java index ae6af3f58b7..c476f1d82c1 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/yaml/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with YAML. */ @Export -@Version("2.21.0") +@Version("2.20.1") package org.apache.logging.log4j.core.config.yaml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java index d3e7133def2..865759ed13f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java @@ -20,8 +20,8 @@ import aQute.bnd.annotation.Resolution; import aQute.bnd.annotation.spi.ServiceConsumer; import java.io.File; +import java.net.URI; import java.time.Instant; -import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -355,26 +355,6 @@ public void watch(final Source source, final Watcher watcher) { watchers.put(source, new ConfigurationMonitor(lastModified, watcher)); } - /** - * Watches the given file. - * - * @param source the source to watch. - * @param auxiliarySources auxiliary sources to also watch - * @param watcher the watcher to notify of file changes. - */ - public void watch( - final Source source, final Collection auxiliarySources, final ConfigurationFileWatcher watcher) { - watcher.watching(source, auxiliarySources); - final long lastModified = watcher.getLastModified(); - if (logger.isDebugEnabled()) { - String message = auxiliarySources.isEmpty() - ? "Watching configuration '{}' for lastModified {} ({})" - : "Watching configuration '{}' for lastModified {} ({}) and files {}"; - logger.debug(message, source, millisToString(lastModified), lastModified); - } - watchers.put(source, new ConfigurationMonitor(lastModified, watcher)); - } - /** * Watches the given file. * @@ -391,4 +371,17 @@ public void watchFile(final File file, final FileWatcher fileWatcher) { final Source source = new Source(file); watch(source, watcher); } + + /** + * Add the given URIs to be monitored for the given source. + * + * @param source the source being watched. + * @param monitorUris the URIs to also watch + */ + public void addMonitorUris(final Source source, final List monitorUris) { + ConfigurationMonitor monitor = watchers.get(source); + if (monitor != null && monitor.getWatcher() instanceof ConfigurationFileWatcher) { + ((ConfigurationFileWatcher) monitor.getWatcher()).addMonitorUris(monitorUris); + } + } } From 443a932aef80730d5077f80c19340372076d41b5 Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Tue, 22 Apr 2025 15:11:56 +0100 Subject: [PATCH 03/12] Updated for review comments Signed-off-by: MichaelMorris --- .../core/config/AbstractConfiguration.java | 13 ++++++- .../core/config/ConfigurationFileWatcher.java | 38 ++++--------------- .../core/config/ConfigurationSource.java | 4 -- .../logging/log4j/core/util/WatchManager.java | 14 ------- 4 files changed, 20 insertions(+), 49 deletions(-) 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 a53dfe2581e..523a70da8ca 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 @@ -331,7 +331,7 @@ public void start() { getConfigurationSource(), uris, watchManager.getIntervalSeconds()); - watchManager.addMonitorUris(configurationSource.getSource(), uris); + watchMonitorUris(); } else { LOGGER.info( "Start watching for changes to {} every {} seconds", @@ -358,6 +358,17 @@ public void start() { LOGGER.info("Configuration {} started.", this); } + private void watchMonitorUris() { + if (this instanceof Reconfigurable) { + uris.stream().forEach(uri -> { + Source source = new Source(uri); + final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher( + this, (Reconfigurable) this, listeners, source.getFile().lastModified()); + watchManager.watch(source, watcher); + }); + } + } + private boolean hasAsyncLoggers() { if (root instanceof AsyncLoggerConfig) { return true; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java index 0dfdb8582fe..9b920a45241 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java @@ -17,10 +17,7 @@ package org.apache.logging.log4j.core.config; import java.io.File; -import java.net.URI; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.logging.log4j.core.util.AbstractWatcher; import org.apache.logging.log4j.core.util.FileWatcher; import org.apache.logging.log4j.core.util.Source; @@ -31,7 +28,8 @@ */ public class ConfigurationFileWatcher extends AbstractWatcher implements FileWatcher { - private Map monitoredFiles = new HashMap<>(); + private File file; + private long lastModifiedMillis; public ConfigurationFileWatcher( final Configuration configuration, @@ -39,49 +37,29 @@ public ConfigurationFileWatcher( final List configurationListeners, long lastModifiedMillis) { super(configuration, reconfigurable, configurationListeners); + this.lastModifiedMillis = lastModifiedMillis; } @Override public long getLastModified() { - Long lastModifiedMillis = 0L; - for (final File monitoredFile : monitoredFiles.keySet()) { - if (monitoredFile.lastModified() > lastModifiedMillis) { - lastModifiedMillis = monitoredFile.lastModified(); - } - } - return lastModifiedMillis; + return file != null ? file.lastModified() : 0; } @Override public void fileModified(final File file) { - monitoredFiles.entrySet().stream() - .forEach(monitoredFile -> - monitoredFile.setValue(monitoredFile.getKey().lastModified())); + lastModifiedMillis = file.lastModified(); } @Override public void watching(final Source source) { - File file = source.getFile(); - monitoredFiles.put(file, file.lastModified()); + file = source.getFile(); + lastModifiedMillis = file.lastModified(); super.watching(source); } - /** - * Add the given URIs to be watched. - * - * @param monitorUris URIs to also watch - */ - public void addMonitorUris(final List monitorUris) { - monitorUris.forEach(uri -> { - File additionalFile = new Source(uri).getFile(); - monitoredFiles.put(additionalFile, additionalFile.lastModified()); - }); - } - @Override public boolean isModified() { - return monitoredFiles.entrySet().stream() - .anyMatch(file -> file.getValue() != file.getKey().lastModified()); + return lastModifiedMillis != file.lastModified(); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java index ecef1c831fa..d6143cdc158 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationSource.java @@ -382,8 +382,4 @@ public String toString() { return null; } } - - Source getSource() { - return this.source; - } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java index 865759ed13f..3f2856340fd 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java @@ -20,7 +20,6 @@ import aQute.bnd.annotation.Resolution; import aQute.bnd.annotation.spi.ServiceConsumer; import java.io.File; -import java.net.URI; import java.time.Instant; import java.util.Date; import java.util.HashMap; @@ -371,17 +370,4 @@ public void watchFile(final File file, final FileWatcher fileWatcher) { final Source source = new Source(file); watch(source, watcher); } - - /** - * Add the given URIs to be monitored for the given source. - * - * @param source the source being watched. - * @param monitorUris the URIs to also watch - */ - public void addMonitorUris(final Source source, final List monitorUris) { - ConfigurationMonitor monitor = watchers.get(source); - if (monitor != null && monitor.getWatcher() instanceof ConfigurationFileWatcher) { - ((ConfigurationFileWatcher) monitor.getWatcher()).addMonitorUris(monitorUris); - } - } } From bf2ad2ced0a415586e07ed2b94684d060608f9d0 Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Mon, 12 May 2025 11:04:50 +0100 Subject: [PATCH 04/12] Update for code review comments and added documentation Signed-off-by: MichaelMorris --- .../core/config/AbstractConfiguration.java | 22 +++---- .../manual/configuration/monitoruris.json | 15 +++++ .../configuration/monitoruris.properties | 20 ++++++ .../manual/configuration/monitoruris.xml | 28 +++++++++ .../manual/configuration/monitoruris.yaml | 22 +++++++ .../ROOT/pages/manual/configuration.adoc | 63 +++++++++++++++++++ 6 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml 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 523a70da8ca..ec7611b508b 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 @@ -269,6 +269,7 @@ public void initialize() { setup(); setupAdvertisement(); doConfigure(); + watchMonitorUris(); setState(State.INITIALIZED); LOGGER.debug("Configuration {} initialized", this); } @@ -325,19 +326,10 @@ public void start() { LOGGER.info("Starting configuration {}...", this); this.setStarting(); if (watchManager.getIntervalSeconds() >= 0) { - if (uris != null && uris.size() > 0) { - LOGGER.info( - "Start watching for changes to {} and {} every {} seconds", - getConfigurationSource(), - uris, - watchManager.getIntervalSeconds()); - watchMonitorUris(); - } else { - LOGGER.info( - "Start watching for changes to {} every {} seconds", - getConfigurationSource(), - watchManager.getIntervalSeconds()); - } + LOGGER.info( + "Start watching for changes to {} every {} seconds", + watchManager.getConfigurationWatchers().keySet(), + watchManager.getIntervalSeconds()); watchManager.start(); } if (hasAsyncLoggers()) { @@ -359,7 +351,7 @@ public void start() { } private void watchMonitorUris() { - if (this instanceof Reconfigurable) { + if (this instanceof Reconfigurable && watchManager.getIntervalSeconds() >= 0) { uris.stream().forEach(uri -> { Source source = new Source(uri); final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher( @@ -802,7 +794,7 @@ private List convertToJavaNetUris(final List uris) { try { javaNetUris.add(new URI(uri.getUri())); } catch (URISyntaxException e) { - LOGGER.error("Error parsing monitor URI: " + uri, e); + throw new ConfigurationException("Invalid URI provided for MonitorUri " + uri); } } return javaNetUris; diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json new file mode 100644 index 00000000000..e8022cc4836 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json @@ -0,0 +1,15 @@ +{ + "Configuration": { + "monitorInterval": "30", + "MonitorUris": { + "Uri": [ + { + "uri": "to-be-monitored-1.file" + }, + { + "uri": "to-be-monitored-2.file" + } + ] + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties new file mode 100644 index 00000000000..1c7010a326c --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties @@ -0,0 +1,20 @@ +# +# 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. +# +## +monitorInterval = 30 +monitorUri.0.uri = to-be-monitored-1.file +monitorUri.1.uri = to-be-monitored-2.file diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml new file mode 100644 index 00000000000..be987894adc --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml new file mode 100644 index 00000000000..a7ee9d371ec --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml @@ -0,0 +1,22 @@ +# +# 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. +# +Configuration: + monitorInterval: '30' + MonitorUris: + Uri: + - uri: to-be-monitored-1.file + - uri: to-be-monitored-2.file diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index 7a0e738e41c..da37190a1c5 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -988,6 +988,69 @@ include::example$manual/configuration/routing.properties[tag=appender] Therefore, the dollar `$` sign needs to be escaped. <2> All the attributes of children of the `Route` element have a **deferred** evaluation. Therefore, they need only one `$` sign. +[#monitorUris] +== Monitor URIs + +Log4j can be configured to poll for changes to resources (in addition to the configuration file) using the `MonitorUris` element of the configuration. If a change is detected in any of the specifed resources, Log4j automatically reconfigures the logger context. + +The polling interval is determined by the value of the <> attribute. If set to 0, polling is disabled. See <> for further details. + +This may be useful where the configuration is dependent on a resource that is external to the configuration file. + +The MonitorUris component supports the following nested element: + +[#MonitorUris-elements] +[cols="1m,1,4"] +|=== +| Type | Multiplicity | Description + +| `Uri` +| one or more +| Specifies the URI of a resouce to be polled for changes. +|=== + +The Uri component supports the following attribute: +[#uri-attributes-name] +=== `uri` + +[cols="1h,5"] +|=== +| Type | `String` +|=== + +See example below: + +[tabs] +==== +XML:: ++ +[source,xml] +---- +include::example$manual/configuration/monitoruris.xml[lines=18..] +---- + +JSON:: ++ +[source,json] +---- +include::example$manual/configuration/monitoruris.json[] +---- + +YAML:: ++ +[source,yaml] +---- +include::example$manual/configuration/monitoruris.yaml[lines=17..] +---- + +Properties:: ++ +[source,properties] +---- +include::example$manual/configuration/monitoruris.properties[lines=18..] +---- +==== + [id=arbiters] == [[Arbiters]] Arbiters From 5afac25bac713abda5eb5742bdecc043b9c9a585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 19 May 2025 21:59:32 +0200 Subject: [PATCH 05/12] Rename `MonitorUris` to `MonitorResources` --- .../log4j/core/MonitorResourcesTest.java | 116 ++++++++++++++++++ .../logging/log4j/core/MonitorUriTest.java | 61 --------- .../config/MonitorResource/log4j.json | 15 +++ .../config/MonitorResource/log4j.properties | 5 +- .../config/MonitorResource/log4j.xml | 8 +- .../config/MonitorResource/log4j.yaml | 8 +- .../core/config/AbstractConfiguration.java | 29 ++--- .../config/{Uri.java => MonitorResource.java} | 56 ++++----- ...MonitorUris.java => MonitorResources.java} | 42 +++---- .../builder/api/ConfigurationBuilder.java | 18 +-- ...a => MonitorResourceComponentBuilder.java} | 2 +- .../builder/impl/BuiltConfiguration.java | 10 +- .../impl/DefaultConfigurationBuilder.java | 18 +-- ...faultMonitorResourceComponentBuilder.java} | 14 +-- .../PropertiesConfigurationBuilder.java | 16 +-- .../.2.x.x/3074_monitor_additional_files.xml | 5 +- .../configuration/monitor-resources.json | 15 +++ .../monitor-resources.properties | 19 +++ .../configuration/monitor-resources.xml | 28 +++++ .../configuration/monitor-resources.yaml | 22 ++++ .../manual/configuration/monitoruris.json | 15 --- .../ROOT/pages/manual/configuration.adoc | 46 ++++--- 22 files changed, 330 insertions(+), 238 deletions(-) create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java delete mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java create mode 100644 log4j-core-test/src/test/resources/config/MonitorResource/log4j.json rename src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties => log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties (87%) rename src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml => log4j-core-test/src/test/resources/config/MonitorResource/log4j.xml (87%) rename src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml => log4j-core-test/src/test/resources/config/MonitorResource/log4j.yaml (85%) rename log4j-core/src/main/java/org/apache/logging/log4j/core/config/{Uri.java => MonitorResource.java} (57%) rename log4j-core/src/main/java/org/apache/logging/log4j/core/config/{MonitorUris.java => MonitorResources.java} (54%) rename log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/{MonitorUriComponentBuilder.java => MonitorResourceComponentBuilder.java} (89%) rename log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/{DefaultMonitorUriComponentBuilder.java => DefaultMonitorResourceComponentBuilder.java} (74%) create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.json create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.xml create mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.yaml delete mode 100644 src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java new file mode 100644 index 00000000000..ee1ae742d37 --- /dev/null +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java @@ -0,0 +1,116 @@ +/* + * 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.core; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.waitAtMost; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; +import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration; +import org.apache.logging.log4j.core.test.junit.LoggerContextSource; +import org.apache.logging.log4j.core.util.Source; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.CleanupMode; +import org.junit.jupiter.api.io.TempDir; + +public class MonitorResourcesTest { + + @Test + void test_reconfiguration(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path tempDir) throws IOException { + final ConfigurationBuilder configBuilder = + ConfigurationBuilderFactory.newConfigurationBuilder(PropertiesConfiguration.class); + final Path configFile = tempDir.resolve("log4j.xml"); + final Path externalResourceFile1 = tempDir.resolve("external-resource-1.txt"); + final Path externalResourceFile2 = tempDir.resolve("external-resource-2.txt"); + final ConfigurationSource configSource = new ConfigurationSource(new Source(configFile), new byte[] {}, 0); + final int monitorInterval = 3; + final Configuration config = configBuilder + .setConfigurationSource(configSource) + .setMonitorInterval(String.valueOf(monitorInterval)) + .add(configBuilder.newMonitorResource( + externalResourceFile1.toUri().toString())) + .add(configBuilder.newMonitorResource( + externalResourceFile2.toUri().toString())) + .build(); + + try (final LoggerContext loggerContext = Configurator.initialize(config)) { + assertMonitorResourceFileNames( + loggerContext, + configFile.getFileName().toString(), + externalResourceFile1.getFileName().toString(), + externalResourceFile2.getFileName().toString()); + Files.write(externalResourceFile2, Collections.singletonList("a change")); + waitAtMost(2 * monitorInterval, TimeUnit.SECONDS).until(() -> loggerContext.getConfiguration() != config); + } + } + + @Test + @LoggerContextSource("config/MonitorResource/log4j.xml") + void test_config_of_type_XML(final LoggerContext loggerContext) { + assertMonitorResourceFileNames(loggerContext, "log4j.xml"); + } + + @Test + @LoggerContextSource("config/MonitorResource/log4j.json") + void test_config_of_type_JSON(final LoggerContext loggerContext) { + assertMonitorResourceFileNames(loggerContext, "log4j.json"); + } + + @Test + @LoggerContextSource("config/MonitorResource/log4j.yaml") + void test_config_of_type_YAML(final LoggerContext loggerContext) { + assertMonitorResourceFileNames(loggerContext, "log4j.yaml"); + } + + @Test + @LoggerContextSource("config/MonitorResource/log4j.properties") + void test_config_of_type_properties(final LoggerContext loggerContext) { + assertMonitorResourceFileNames(loggerContext, "log4j.properties"); + } + + private static void assertMonitorResourceFileNames(final LoggerContext loggerContext, final String configFileName) { + assertMonitorResourceFileNames(loggerContext, configFileName, "external-file-1.txt", "external-file-2.txt"); + } + + private static void assertMonitorResourceFileNames( + final LoggerContext loggerContext, final String configFileName, final String... externalResourceFileNames) { + final Set sources = loggerContext + .getConfiguration() + .getWatchManager() + .getConfigurationWatchers() + .keySet(); + final Set actualFileNames = + sources.stream().map(source -> source.getFile().getName()).collect(Collectors.toSet()); + final Set expectedFileNames = new LinkedHashSet<>(); + expectedFileNames.add(configFileName); + expectedFileNames.addAll(Arrays.asList(externalResourceFileNames)); + assertThat(actualFileNames).as("watch manager sources: %s", sources).isEqualTo(expectedFileNames); + } +} diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java deleted file mode 100644 index 20c519afa55..00000000000 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorUriTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.core; - -import static org.awaitility.Awaitility.waitAtMost; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.concurrent.TimeUnit; -import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.ConfigurationSource; -import org.apache.logging.log4j.core.config.Configurator; -import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; -import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration; -import org.apache.logging.log4j.core.util.Source; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.CleanupMode; -import org.junit.jupiter.api.io.TempDir; - -public class MonitorUriTest { - - private static final int MONITOR_INTERVAL = 3; - - @Test - void testReconfigureOnChangeInMonitorUri(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path tempDir) - throws IOException { - ConfigurationBuilder configBuilder = - ConfigurationBuilderFactory.newConfigurationBuilder(PropertiesConfiguration.class); - Path config = tempDir.resolve("config.xml"); - Path monitorUri = tempDir.resolve("monitorUri.xml"); - ConfigurationSource configSource = new ConfigurationSource(new Source(config), new byte[] {}, 0); - Configuration configuration = configBuilder - .setConfigurationSource(configSource) - .setMonitorInterval(String.valueOf(MONITOR_INTERVAL)) - .add(configBuilder.newMonitorUri(monitorUri.toUri().toString())) - .build(); - - try (LoggerContext loggerContext = Configurator.initialize(configuration)) { - Files.write(monitorUri, Collections.singletonList("a change")); - waitAtMost(MONITOR_INTERVAL + 2, TimeUnit.SECONDS) - .until(() -> loggerContext.getConfiguration() != configuration); - } - } -} diff --git a/log4j-core-test/src/test/resources/config/MonitorResource/log4j.json b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.json new file mode 100644 index 00000000000..26c566af41b --- /dev/null +++ b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.json @@ -0,0 +1,15 @@ +{ + "Configuration": { + "monitorInterval": "30", + "MonitorResources": { + "MonitorResource": [ + { + "uri": "file://path/to/external-file-1.txt" + }, + { + "uri": "file://path/to/external-file-2.txt" + } + ] + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties similarity index 87% rename from src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties rename to log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties index 1c7010a326c..7546b197b04 100644 --- a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.properties +++ b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -## monitorInterval = 30 -monitorUri.0.uri = to-be-monitored-1.file -monitorUri.1.uri = to-be-monitored-2.file +monitorResources.0.uri = file://path/to/external-file-1.txt +monitorResources.1.uri = file://path/to/external-file-2.txt diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.xml similarity index 87% rename from src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml rename to log4j-core-test/src/test/resources/config/MonitorResource/log4j.xml index be987894adc..1529167d56f 100644 --- a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.xml +++ b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.xml @@ -21,8 +21,8 @@ https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd" monitorInterval="30"> - - - - + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.yaml similarity index 85% rename from src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml rename to log4j-core-test/src/test/resources/config/MonitorResource/log4j.yaml index a7ee9d371ec..11f804108b7 100644 --- a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.yaml +++ b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.yaml @@ -16,7 +16,7 @@ # Configuration: monitorInterval: '30' - MonitorUris: - Uri: - - uri: to-be-monitored-1.file - - uri: to-be-monitored-2.file + MonitorResources: + MonitorResource: + - uri: "file://path/to/external-file-1.txt" + - uri: "file://path/to/external-file-2.txt" 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 ec7611b508b..b3300269f32 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 @@ -22,7 +22,6 @@ import java.io.InputStream; import java.lang.ref.WeakReference; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -133,7 +132,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement private ConcurrentMap appenders = new ConcurrentHashMap<>(); private ConcurrentMap loggerConfigs = new ConcurrentHashMap<>(); private List customLevels = Collections.emptyList(); - private List uris = Collections.emptyList(); + private Set monitorResources = Collections.emptySet(); private final ConcurrentMap propertyMap = new ConcurrentHashMap<>(); private final Interpolator tempLookup = new Interpolator(propertyMap); private final StrSubstitutor runtimeStrSubstitutor = new RuntimeStrSubstitutor(tempLookup); @@ -269,7 +268,7 @@ public void initialize() { setup(); setupAdvertisement(); doConfigure(); - watchMonitorUris(); + watchMonitorResources(); setState(State.INITIALIZED); LOGGER.debug("Configuration {} initialized", this); } @@ -350,10 +349,10 @@ public void start() { LOGGER.info("Configuration {} started.", this); } - private void watchMonitorUris() { + private void watchMonitorResources() { if (this instanceof Reconfigurable && watchManager.getIntervalSeconds() >= 0) { - uris.stream().forEach(uri -> { - Source source = new Source(uri); + monitorResources.forEach(monitorResource -> { + Source source = new Source(monitorResource.getUri()); final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher( this, (Reconfigurable) this, listeners, source.getFile().lastModified()); watchManager.watch(source, watcher); @@ -743,8 +742,8 @@ protected void doConfigure() { } else if (child.isInstanceOf(AsyncWaitStrategyFactoryConfig.class)) { final AsyncWaitStrategyFactoryConfig awsfc = child.getObject(AsyncWaitStrategyFactoryConfig.class); asyncWaitStrategyFactory = awsfc.createWaitStrategyFactory(); - } else if (child.isInstanceOf(MonitorUris.class)) { - uris = convertToJavaNetUris(child.getObject(MonitorUris.class).getUris()); + } else if (child.isInstanceOf(MonitorResources.class)) { + monitorResources = child.getObject(MonitorResources.class).getResources(); } else { final List expected = Arrays.asList( "\"Appenders\"", @@ -752,7 +751,7 @@ protected void doConfigure() { "\"Properties\"", "\"Scripts\"", "\"CustomLevels\"", - "\"MonitorUris\""); + "\"MonitorResources\""); LOGGER.error( "Unknown object \"{}\" of type {} is ignored: try nesting it inside one of: {}.", child.getName(), @@ -788,18 +787,6 @@ protected void doConfigure() { setParents(); } - private List convertToJavaNetUris(final List uris) { - final List javaNetUris = new ArrayList<>(); - for (Uri uri : uris) { - try { - javaNetUris.add(new URI(uri.getUri())); - } catch (URISyntaxException e) { - throw new ConfigurationException("Invalid URI provided for MonitorUri " + uri); - } - } - return javaNetUris; - } - public static Level getDefaultLevel() { final String levelName = PropertiesUtil.getProperties() .getStringProperty(DefaultConfiguration.DEFAULT_LEVEL, Level.ERROR.name()); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Uri.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java similarity index 57% rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/Uri.java rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java index a07dc3e2a03..685ecbf697a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Uri.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java @@ -16,51 +16,37 @@ */ package org.apache.logging.log4j.core.config; -import java.util.Objects; +import static java.util.Objects.requireNonNull; + +import java.net.URI; import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; import org.apache.logging.log4j.core.config.plugins.PluginFactory; -import org.apache.logging.log4j.core.config.plugins.PluginValue; -import org.apache.logging.log4j.status.StatusLogger; /** - * Descriptor of a URI object that is created via configuration. + * Container for the {@code MonitorResource} element. */ -@Plugin(name = "Uri", category = Core.CATEGORY_NAME, printObject = true) -public final class Uri { - - /** - * The empty array. - */ - static final Uri[] EMPTY_ARRAY = {}; +@Plugin(name = "MonitorResource", category = Core.CATEGORY_NAME, printObject = true) +public final class MonitorResource { - private final String uri; + private final URI uri; - private Uri(final String uri) { - this.uri = Objects.requireNonNull(uri, "uri is null"); + private MonitorResource(final URI uri) { + this.uri = requireNonNull(uri, "uri"); + if (!"file".equals(uri.getScheme())) { + final String message = + String.format("Only `file` scheme is supported in monitor resource URIs! Illegal URI: `%s`", uri); + throw new IllegalArgumentException(message); + } } - /** - * Creates a Uri object. - * - * @param uri the URI. - * @return A Uri object. - */ @PluginFactory - public static Uri createUri( // @formatter:off - @PluginValue("uri") final String uri) { - // @formatter:on - - StatusLogger.getLogger().debug("Creating Uri('{}')", uri); - return new Uri(uri); + public static MonitorResource createMonitorResource(@PluginAttribute("uri") final URI uri) { + return new MonitorResource(uri); } - /** - * Returns the URI. - * - * @return the URI - */ - public String getUri() { + public URI getUri() { return uri; } @@ -74,15 +60,15 @@ public boolean equals(final Object object) { if (this == object) { return true; } - if (!(object instanceof Uri)) { + if (!(object instanceof MonitorResource)) { return false; } - final Uri other = (Uri) object; + final MonitorResource other = (MonitorResource) object; return this.uri == other.uri; } @Override public String toString() { - return "Uri[" + uri + "]"; + return String.format("MonitorResource{%s}", uri); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorUris.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResources.java similarity index 54% rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorUris.java rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResources.java index d294e8fa083..f7cc5e20260 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorUris.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResources.java @@ -16,43 +16,39 @@ */ package org.apache.logging.log4j.core.config; -import java.util.ArrayList; +import static java.util.Objects.requireNonNull; + import java.util.Arrays; -import java.util.List; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginElement; import org.apache.logging.log4j.core.config.plugins.PluginFactory; /** - * Container for MonitorUri objects. + * Container for the {@code MonitorResources} element. */ -@Plugin(name = "MonitorUris", category = Core.CATEGORY_NAME, printObject = true) -public final class MonitorUris { +@Plugin(name = "MonitorResources", category = Core.CATEGORY_NAME, printObject = true) +public final class MonitorResources { - private final List uris; + private final Set resources; - private MonitorUris(final Uri[] uris) { - this.uris = new ArrayList<>(Arrays.asList(uris)); + private MonitorResources(final Set resources) { + this.resources = requireNonNull(resources, "resources"); } - /** - * Create a MonitorUris object to contain all the URIs to be monitored. - * - * @param uris An array of URIs. - * @return A MonitorUris object. - */ @PluginFactory - public static MonitorUris createMonitorUris( // - @PluginElement("Uris") final Uri[] uris) { - return new MonitorUris(uris == null ? Uri.EMPTY_ARRAY : uris); + public static MonitorResources createMonitorResources( + @PluginElement("monitorResource") final MonitorResource[] resources) { + requireNonNull(resources, "resources"); + final LinkedHashSet distinctResources = + Arrays.stream(resources).collect(Collectors.toCollection(LinkedHashSet::new)); + return new MonitorResources(distinctResources); } - /** - * Returns a list of the {@code Uri} objects created during configuration. - * @return the URIs to be monitored - */ - public List getUris() { - return uris; + public Set getResources() { + return resources; } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java index badbd346cda..5dac2298d4b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java @@ -61,13 +61,8 @@ public interface ConfigurationBuilder extends Builder add(CustomLevelComponentBuilder builder); - /** - * Adds a MonitorUri component. - * @param builder The MonitorUriComponentBuilder with all of its attributes set. - * @return this builder instance. - */ - default ConfigurationBuilder add(MonitorUriComponentBuilder builder) { - return this; + default ConfigurationBuilder add(MonitorResourceComponentBuilder builder) { + throw new UnsupportedOperationException(); } /** @@ -281,13 +276,8 @@ default ConfigurationBuilder add(MonitorUriComponentBuilder builder) { */ CustomLevelComponentBuilder newCustomLevel(String name, int level); - /** - * Returns a builder for creating MonitorUris - * @param uri The URI. - * @return A new MonitorUriComponentBuilder. - */ - default MonitorUriComponentBuilder newMonitorUri(String uri) { - return null; + default MonitorResourceComponentBuilder newMonitorResource(String uri) { + throw new UnsupportedOperationException(); } /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorUriComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorResourceComponentBuilder.java similarity index 89% rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorUriComponentBuilder.java rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorResourceComponentBuilder.java index 4942dc02c37..39ad975344b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorUriComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorResourceComponentBuilder.java @@ -19,4 +19,4 @@ /** * Assembler for constructing MonitorUri Components. */ -public interface MonitorUriComponentBuilder extends ComponentBuilder {} +public interface MonitorResourceComponentBuilder extends ComponentBuilder {} 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 8cf02dde440..e47b3b7b6a2 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 @@ -46,7 +46,7 @@ public class BuiltConfiguration extends AbstractConfiguration { private Component propertiesComponent; private Component customLevelsComponent; private Component scriptsComponent; - private Component monitorUriComponent; + private Component monitorResourcesComponent; private String contentType = "text"; public BuiltConfiguration( @@ -79,8 +79,8 @@ public BuiltConfiguration( customLevelsComponent = component; break; } - case "MonitorUris": { - monitorUriComponent = component; + case "MonitorResources": { + monitorResourcesComponent = component; break; } } @@ -100,8 +100,8 @@ public void setup() { if (customLevelsComponent.getComponents().size() > 0) { children.add(convertToNode(rootNode, customLevelsComponent)); } - if (monitorUriComponent.getComponents().size() > 0) { - children.add(convertToNode(rootNode, monitorUriComponent)); + if (monitorResourcesComponent.getComponents().size() > 0) { + children.add(convertToNode(rootNode, monitorResourcesComponent)); } children.add(convertToNode(rootNode, loggersComponent)); children.add(convertToNode(rootNode, appendersComponent)); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java index 1dc4a66ac1e..5a161fcd74c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java @@ -55,7 +55,7 @@ import org.apache.logging.log4j.core.config.builder.api.KeyValuePairComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; -import org.apache.logging.log4j.core.config.builder.api.MonitorUriComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.MonitorResourceComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.PropertyComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; @@ -78,7 +78,7 @@ public class DefaultConfigurationBuilder implement private Component properties; private Component customLevels; private Component scripts; - private Component monitorUris; + private final Component monitorResources; private final Class clazz; private ConfigurationSource source; private int monitorInterval; @@ -126,8 +126,8 @@ public DefaultConfigurationBuilder(final Class clazz) { components.add(appenders); loggers = new Component("Loggers"); components.add(loggers); - monitorUris = new Component("MonitorUris"); - components.add(monitorUris); + monitorResources = new Component("MonitorResources"); + components.add(monitorResources); } protected ConfigurationBuilder add(final Component parent, final ComponentBuilder builder) { @@ -141,8 +141,8 @@ public ConfigurationBuilder add(final AppenderComponentBuilder builder) { } @Override - public ConfigurationBuilder add(final MonitorUriComponentBuilder builder) { - return add(monitorUris, builder); + public ConfigurationBuilder add(final MonitorResourceComponentBuilder builder) { + return add(monitorResources, builder); } @Override @@ -300,7 +300,7 @@ private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLSt writeXmlSection(xmlWriter, properties); writeXmlSection(xmlWriter, scripts); writeXmlSection(xmlWriter, customLevels); - writeXmlSection(xmlWriter, monitorUris); + writeXmlComponent(xmlWriter, monitorResources); if (filters.getComponents().size() == 1) { writeXmlComponent(xmlWriter, filters.getComponents().get(0)); } else if (filters.getComponents().size() > 1) { @@ -463,8 +463,8 @@ public CustomLevelComponentBuilder newCustomLevel(final String name, final int l } @Override - public MonitorUriComponentBuilder newMonitorUri(final String uri) { - return new DefaultMonitorUriComponentBuilder(this, uri); + public MonitorResourceComponentBuilder newMonitorResource(final String uri) { + return new DefaultMonitorResourceComponentBuilder(this, uri); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorUriComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorResourceComponentBuilder.java similarity index 74% rename from log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorUriComponentBuilder.java rename to log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorResourceComponentBuilder.java index 21299003cf8..d6942e66a9c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorUriComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorResourceComponentBuilder.java @@ -17,17 +17,15 @@ package org.apache.logging.log4j.core.config.builder.impl; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.builder.api.MonitorUriComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.MonitorResourceComponentBuilder; -/** - * - */ -class DefaultMonitorUriComponentBuilder extends DefaultComponentAndConfigurationBuilder - implements MonitorUriComponentBuilder { +class DefaultMonitorResourceComponentBuilder + extends DefaultComponentAndConfigurationBuilder + implements MonitorResourceComponentBuilder { - public DefaultMonitorUriComponentBuilder( + public DefaultMonitorResourceComponentBuilder( final DefaultConfigurationBuilder builder, final String uri) { - super(builder, "Uri"); + super(builder, "MonitorResource"); addAttribute("uri", uri); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java index 8f7cb3e5b1b..2a2666d82b9 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java @@ -35,7 +35,7 @@ import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggableComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; -import org.apache.logging.log4j.core.config.builder.api.MonitorUriComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.MonitorResourceComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; @@ -125,10 +125,10 @@ public PropertiesConfiguration build() { } } - final Map monitorUris = - PropertiesUtil.partitionOnCommonPrefixes(PropertiesUtil.extractSubset(rootProperties, "monitorUri")); - for (final Map.Entry entry : monitorUris.entrySet()) { - builder.add(createMonitorUri(entry.getKey().trim(), entry.getValue())); + final Map monitorResources = PropertiesUtil.partitionOnCommonPrefixes( + PropertiesUtil.extractSubset(rootProperties, "monitorResources")); + for (final Map.Entry entry : monitorResources.entrySet()) { + builder.add(createMonitorResource(entry.getKey().trim(), entry.getValue())); } final String filterProp = rootProperties.getProperty("filters"); @@ -257,12 +257,12 @@ private AppenderRefComponentBuilder createAppenderRef(final String key, final Pr return addFiltersToComponent(appenderRefBuilder, properties); } - private MonitorUriComponentBuilder createMonitorUri(final String key, final Properties properties) { + private MonitorResourceComponentBuilder createMonitorResource(final String key, final Properties properties) { final String uri = (String) properties.remove("uri"); if (Strings.isEmpty(uri)) { - throw new ConfigurationException("No uri attribute provided for MonitorUri " + key); + throw new ConfigurationException("No uri attribute provided for MonitorResource " + key); } - return builder.newMonitorUri(uri); + return builder.newMonitorResource(uri); } private LoggerComponentBuilder createLogger(final String key, final Properties properties) { diff --git a/src/changelog/.2.x.x/3074_monitor_additional_files.xml b/src/changelog/.2.x.x/3074_monitor_additional_files.xml index dcc2d028def..c32545a602a 100644 --- a/src/changelog/.2.x.x/3074_monitor_additional_files.xml +++ b/src/changelog/.2.x.x/3074_monitor_additional_files.xml @@ -2,7 +2,8 @@ + type="added"> - Support configuration option to provide URIs to monitor in addition to the config file. + + Add `MonitorResource` configuration option to support the monitoring of external files in addition to the configuration file itself. diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.json b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.json new file mode 100644 index 00000000000..26c566af41b --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.json @@ -0,0 +1,15 @@ +{ + "Configuration": { + "monitorInterval": "30", + "MonitorResources": { + "MonitorResource": [ + { + "uri": "file://path/to/external-file-1.txt" + }, + { + "uri": "file://path/to/external-file-2.txt" + } + ] + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties new file mode 100644 index 00000000000..7546b197b04 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties @@ -0,0 +1,19 @@ +# +# 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. +# +monitorInterval = 30 +monitorResources.0.uri = file://path/to/external-file-1.txt +monitorResources.1.uri = file://path/to/external-file-2.txt diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.xml b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.xml new file mode 100644 index 00000000000..1529167d56f --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.yaml b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.yaml new file mode 100644 index 00000000000..11f804108b7 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.yaml @@ -0,0 +1,22 @@ +# +# 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. +# +Configuration: + monitorInterval: '30' + MonitorResources: + MonitorResource: + - uri: "file://path/to/external-file-1.txt" + - uri: "file://path/to/external-file-2.txt" diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json b/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json deleted file mode 100644 index e8022cc4836..00000000000 --- a/src/site/antora/modules/ROOT/examples/manual/configuration/monitoruris.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Configuration": { - "monitorInterval": "30", - "MonitorUris": { - "Uri": [ - { - "uri": "to-be-monitored-1.file" - }, - { - "uri": "to-be-monitored-2.file" - } - ] - } - } -} diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc index da37190a1c5..d7f90e6c209 100644 --- a/src/site/antora/modules/ROOT/pages/manual/configuration.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/configuration.adoc @@ -988,66 +988,62 @@ include::example$manual/configuration/routing.properties[tag=appender] Therefore, the dollar `$` sign needs to be escaped. <2> All the attributes of children of the `Route` element have a **deferred** evaluation. Therefore, they need only one `$` sign. -[#monitorUris] -== Monitor URIs +[#monitorResources] +== Monitor Resources -Log4j can be configured to poll for changes to resources (in addition to the configuration file) using the `MonitorUris` element of the configuration. If a change is detected in any of the specifed resources, Log4j automatically reconfigures the logger context. +Log4j can be configured to poll for changes to resources (in addition to the configuration file) using the `MonitorResources` element of the configuration. +If a change is detected in any of the specified resources, Log4j automatically reconfigures the logger context. +This feature helps with monitoring external resources (e.g., TLS certificates) that the configuration is dependent on. -The polling interval is determined by the value of the <> attribute. If set to 0, polling is disabled. See <> for further details. +The polling interval is determined by the value of the <> attribute. +If set to 0, polling is disabled. +See <> for further details. -This may be useful where the configuration is dependent on a resource that is external to the configuration file. +A configuration can have either zero or one `MonitorResources` element at its root. +`MonitorResources` can have zero or more `MonitorResource` elements, which can be configured following attributes: -The MonitorUris component supports the following nested element: - -[#MonitorUris-elements] +.`MonitorResource` configuration attributes [cols="1m,1,4"] |=== -| Type | Multiplicity | Description - -| `Uri` -| one or more -| Specifies the URI of a resouce to be polled for changes. -|=== - -The Uri component supports the following attribute: -[#uri-attributes-name] -=== `uri` +| Attribute | Type | Description -[cols="1h,5"] -|=== -| Type | `String` +| `uri` +| URI +a| A https://docs.oracle.com/javase/{java-target-version}/docs/api/java/net/URI.html[`java.net.URI`] reference to the external resource. +Note that only URIs of scheme `file` are accepted. |=== See example below: +.`MonitorResources` configuration example [tabs] ==== XML:: + [source,xml] ---- -include::example$manual/configuration/monitoruris.xml[lines=18..] +include::example$manual/configuration/monitor-resources.xml[lines=18..] ---- JSON:: + [source,json] ---- -include::example$manual/configuration/monitoruris.json[] +include::example$manual/configuration/monitor-resources.json[] ---- YAML:: + [source,yaml] ---- -include::example$manual/configuration/monitoruris.yaml[lines=17..] +include::example$manual/configuration/monitor-resources.yaml[lines=17..] ---- Properties:: + [source,properties] ---- -include::example$manual/configuration/monitoruris.properties[lines=18..] +include::example$manual/configuration/monitor-resources.properties[lines=18..] ---- ==== From 6f55ae74b3d73b0f4f9edd10205ceee26880fc35 Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Thu, 22 May 2025 11:03:01 +0100 Subject: [PATCH 06/12] Updated for review comments and failing test case Signed-off-by: MichaelMorris --- .../config/MonitorResource/log4j.properties | 3 +++ .../core/config/AbstractConfiguration.java | 8 +++++-- .../impl/DefaultConfigurationBuilder.java | 4 +++- .../PropertiesConfigurationBuilder.java | 22 +++++++++++++++---- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties index 7546b197b04..dd772d9ff10 100644 --- a/log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties +++ b/log4j-core-test/src/test/resources/config/MonitorResource/log4j.properties @@ -15,5 +15,8 @@ # limitations under the License. # monitorInterval = 30 +monitorResources.type = MonitorResources +monitorResources.0.type = MonitorResource monitorResources.0.uri = file://path/to/external-file-1.txt +monitorResources.1.type = MonitorResource monitorResources.1.uri = file://path/to/external-file-2.txt 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 b3300269f32..41fad82cd3f 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 @@ -324,7 +324,7 @@ public void start() { } LOGGER.info("Starting configuration {}...", this); this.setStarting(); - if (watchManager.getIntervalSeconds() >= 0) { + if (isConfigurationMonitoringEnabled()) { LOGGER.info( "Start watching for changes to {} every {} seconds", watchManager.getConfigurationWatchers().keySet(), @@ -349,8 +349,12 @@ public void start() { LOGGER.info("Configuration {} started.", this); } + private boolean isConfigurationMonitoringEnabled() { + return this instanceof Reconfigurable && watchManager.getIntervalSeconds() > 0; + } + private void watchMonitorResources() { - if (this instanceof Reconfigurable && watchManager.getIntervalSeconds() >= 0) { + if (isConfigurationMonitoringEnabled()) { monitorResources.forEach(monitorResource -> { Source source = new Source(monitorResource.getUri()); final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher( diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java index 5a161fcd74c..bf7c19e3f4e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java @@ -300,7 +300,9 @@ private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLSt writeXmlSection(xmlWriter, properties); writeXmlSection(xmlWriter, scripts); writeXmlSection(xmlWriter, customLevels); - writeXmlComponent(xmlWriter, monitorResources); + if (!monitorResources.getComponents().isEmpty()) { + writeXmlComponent(xmlWriter, monitorResources); + } if (filters.getComponents().size() == 1) { writeXmlComponent(xmlWriter, filters.getComponents().get(0)); } else if (filters.getComponents().size() > 1) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java index 2a2666d82b9..eefaedf27c1 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java @@ -125,10 +125,24 @@ public PropertiesConfiguration build() { } } - final Map monitorResources = PropertiesUtil.partitionOnCommonPrefixes( - PropertiesUtil.extractSubset(rootProperties, "monitorResources")); - for (final Map.Entry entry : monitorResources.entrySet()) { - builder.add(createMonitorResource(entry.getKey().trim(), entry.getValue())); + Properties monitorResources = PropertiesUtil.extractSubset(rootProperties, "monitorResources"); + if (monitorResources.size() > 0) { + final String monitorResourcesType = (String) monitorResources.remove("type"); + if (!"MonitorResources".equals(monitorResourcesType)) { + throw new ConfigurationException( + "No or invalid type provided for monitorResouces - must be MonitorResources"); + } + final Map monitorResourceMap = + PropertiesUtil.partitionOnCommonPrefixes(monitorResources); + for (final Map.Entry entry : monitorResourceMap.entrySet()) { + final Properties monitorResourceProps = entry.getValue(); + final String monitorResourceType = (String) monitorResourceProps.remove("type"); + if (!"MonitorResource".equals(monitorResourceType)) { + throw new ConfigurationException( + "No or invalid type provided for monitorResouce - must be MonitorResource"); + } + builder.add(createMonitorResource(entry.getKey().trim(), entry.getValue())); + } } final String filterProp = rootProperties.getProperty("filters"); From 3d16973773d2c9b638e8c5c48a884295515a5fb8 Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Tue, 27 May 2025 21:50:24 +0100 Subject: [PATCH 07/12] Updated for code review comments - Use builder pattern for MontiorResource - Handle AsyncWaitStrategyFactory in properties file as well, in a generic way Signed-off-by: MichaelMorris --- .../log4j/core/MonitorResourcesTest.java | 17 ++++-- .../AsyncWaitStrategyFactoryConfigTest.java | 11 ++++ ...ncWaitStrategyFactoryConfigTest.properties | 20 +++++++ .../log4j/core/config/MonitorResource.java | 35 +++++++++--- .../builder/api/ConfigurationBuilder.java | 11 ++-- .../builder/impl/BuiltConfiguration.java | 11 +++- .../impl/DefaultConfigurationBuilder.java | 30 +++++----- .../PropertiesConfigurationBuilder.java | 55 +++++++++---------- 8 files changed, 130 insertions(+), 60 deletions(-) create mode 100644 log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java index ee1ae742d37..923d1022c67 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java @@ -31,6 +31,7 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration; @@ -51,13 +52,21 @@ void test_reconfiguration(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path final Path externalResourceFile2 = tempDir.resolve("external-resource-2.txt"); final ConfigurationSource configSource = new ConfigurationSource(new Source(configFile), new byte[] {}, 0); final int monitorInterval = 3; + + ComponentBuilder monitorResourcesComponent = configBuilder.newComponent("MonitorResources"); + monitorResourcesComponent.addComponent(monitorResourcesComponent + .getBuilder() + .newComponent("MonitorResource") + .addAttribute("uri", externalResourceFile1.toUri().toString())); + monitorResourcesComponent.addComponent(monitorResourcesComponent + .getBuilder() + .newComponent("MonitorResource") + .addAttribute("uri", externalResourceFile2.toUri().toString())); + final Configuration config = configBuilder .setConfigurationSource(configSource) .setMonitorInterval(String.valueOf(monitorInterval)) - .add(configBuilder.newMonitorResource( - externalResourceFile1.toUri().toString())) - .add(configBuilder.newMonitorResource( - externalResourceFile2.toUri().toString())) + .addComponent(monitorResourcesComponent) .build(); try (final LoggerContext loggerContext = Configurator.initialize(config)) { diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java index 42cdcb3071c..71710b08bfd 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java @@ -44,6 +44,17 @@ void testConfigWaitStrategyFactory(final LoggerContext context) { "factory is YieldingWaitStrategyFactory", asyncWaitStrategyFactory instanceof YieldingWaitStrategyFactory); } + + @Test + @LoggerContextSource("AsyncWaitStrategyFactoryConfigTest.properties") + void testConfigWaitStrategyFactoryFromProperties(final LoggerContext context) { + final AsyncWaitStrategyFactory asyncWaitStrategyFactory = + context.getConfiguration().getAsyncWaitStrategyFactory(); + assertEquals(YieldingWaitStrategyFactory.class, asyncWaitStrategyFactory.getClass()); + assertThat( + "factory is YieldingWaitStrategyFactory", + asyncWaitStrategyFactory instanceof YieldingWaitStrategyFactory); + } @Test @LoggerContextSource("AsyncWaitStrategyFactoryConfigTest.xml") diff --git a/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties b/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties new file mode 100644 index 00000000000..9414ba1b509 --- /dev/null +++ b/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties @@ -0,0 +1,20 @@ +# +# 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. +# + +strategy.type = AsyncWaitStrategyFactory +strategy.class = org.apache.logging.log4j.core.async.AsyncWaitStrategyFactoryConfigTest$YieldingWaitStrategyFactory + diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java index 685ecbf697a..a9417a81bad 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java @@ -21,8 +21,9 @@ import java.net.URI; import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.config.plugins.PluginAttribute; -import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; /** * Container for the {@code MonitorResource} element. @@ -32,6 +33,31 @@ public final class MonitorResource { private final URI uri; + @PluginBuilderFactory + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builds MonitorResource instances. + */ + public static class Builder implements org.apache.logging.log4j.core.util.Builder { + + @PluginBuilderAttribute + @Required(message = "No URI provided") + private URI uri; + + public Builder withUri(final URI uri) { + this.uri = uri; + return this; + } + + @Override + public MonitorResource build() { + return new MonitorResource(uri); + } + } + private MonitorResource(final URI uri) { this.uri = requireNonNull(uri, "uri"); if (!"file".equals(uri.getScheme())) { @@ -41,11 +67,6 @@ private MonitorResource(final URI uri) { } } - @PluginFactory - public static MonitorResource createMonitorResource(@PluginAttribute("uri") final URI uri) { - return new MonitorResource(uri); - } - public URI getUri() { return uri; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java index 5dac2298d4b..9fb9a3f5def 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java @@ -61,7 +61,12 @@ public interface ConfigurationBuilder extends Builder add(CustomLevelComponentBuilder builder); - default ConfigurationBuilder add(MonitorResourceComponentBuilder builder) { + /** + * Adds a top level component. + * @param builder The ComponentBuilder with all of its attributes and sub components set. + * @return this builder instance. + */ + default ConfigurationBuilder addComponent(ComponentBuilder builder) { throw new UnsupportedOperationException(); } @@ -276,10 +281,6 @@ default ConfigurationBuilder add(MonitorResourceComponentBuilder builder) { */ CustomLevelComponentBuilder newCustomLevel(String name, int level); - default MonitorResourceComponentBuilder newMonitorResource(String uri) { - throw new UnsupportedOperationException(); - } - /** * Returns a builder for creating Filters. * @param pluginName The Plugin type of the Filter. 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 e47b3b7b6a2..8445dc706a4 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 @@ -47,6 +47,7 @@ public class BuiltConfiguration extends AbstractConfiguration { private Component customLevelsComponent; private Component scriptsComponent; private Component monitorResourcesComponent; + private Component asyncWaitStrategyFactoryComponent; private String contentType = "text"; public BuiltConfiguration( @@ -83,6 +84,10 @@ public BuiltConfiguration( monitorResourcesComponent = component; break; } + case "AsyncWaitStrategyFactory": { + asyncWaitStrategyFactoryComponent = component; + break; + } } } this.rootComponent = rootComponent; @@ -100,9 +105,13 @@ public void setup() { if (customLevelsComponent.getComponents().size() > 0) { children.add(convertToNode(rootNode, customLevelsComponent)); } - if (monitorResourcesComponent.getComponents().size() > 0) { + if (monitorResourcesComponent != null + && monitorResourcesComponent.getComponents().size() > 0) { children.add(convertToNode(rootNode, monitorResourcesComponent)); } + if (asyncWaitStrategyFactoryComponent != null) { + children.add(convertToNode(rootNode, asyncWaitStrategyFactoryComponent)); + } children.add(convertToNode(rootNode, loggersComponent)); children.add(convertToNode(rootNode, appendersComponent)); if (filtersComponent.getComponents().size() > 0) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java index bf7c19e3f4e..bf039a2dafc 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java @@ -24,6 +24,7 @@ import java.lang.reflect.Constructor; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.TimeUnit; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; @@ -55,7 +56,6 @@ import org.apache.logging.log4j.core.config.builder.api.KeyValuePairComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; -import org.apache.logging.log4j.core.config.builder.api.MonitorResourceComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.PropertyComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; @@ -78,7 +78,6 @@ public class DefaultConfigurationBuilder implement private Component properties; private Component customLevels; private Component scripts; - private final Component monitorResources; private final Class clazz; private ConfigurationSource source; private int monitorInterval; @@ -126,8 +125,6 @@ public DefaultConfigurationBuilder(final Class clazz) { components.add(appenders); loggers = new Component("Loggers"); components.add(loggers); - monitorResources = new Component("MonitorResources"); - components.add(monitorResources); } protected ConfigurationBuilder add(final Component parent, final ComponentBuilder builder) { @@ -141,8 +138,14 @@ public ConfigurationBuilder add(final AppenderComponentBuilder builder) { } @Override - public ConfigurationBuilder add(final MonitorResourceComponentBuilder builder) { - return add(monitorResources, builder); + public ConfigurationBuilder addComponent(ComponentBuilder builder) { + return add(root, builder); + } + + private Optional getTopLevelComponent(final String pluginType) { + return root.getComponents().stream() + .filter(component -> component.getPluginType().equals(pluginType)) + .findAny(); } @Override @@ -300,8 +303,14 @@ private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLSt writeXmlSection(xmlWriter, properties); writeXmlSection(xmlWriter, scripts); writeXmlSection(xmlWriter, customLevels); - if (!monitorResources.getComponents().isEmpty()) { - writeXmlComponent(xmlWriter, monitorResources); + Optional monitorResourcesComponent = getTopLevelComponent("MonitorResources"); + if (monitorResourcesComponent.isPresent() + && !monitorResourcesComponent.get().getComponents().isEmpty()) { + writeXmlComponent(xmlWriter, monitorResourcesComponent.get()); + } + Optional asyncWaitStrategyFactoryComponent = getTopLevelComponent("AsyncWaitStrategyFactory"); + if (asyncWaitStrategyFactoryComponent.isPresent()) { + writeXmlComponent(xmlWriter, asyncWaitStrategyFactoryComponent.get()); } if (filters.getComponents().size() == 1) { writeXmlComponent(xmlWriter, filters.getComponents().get(0)); @@ -464,11 +473,6 @@ public CustomLevelComponentBuilder newCustomLevel(final String name, final int l return new DefaultCustomLevelComponentBuilder(this, name, level); } - @Override - public MonitorResourceComponentBuilder newMonitorResource(final String uri) { - return new DefaultMonitorResourceComponentBuilder(this, uri); - } - @Override public FilterComponentBuilder newFilter( final String type, final Filter.Result onMatch, final Filter.Result onMismatch) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java index eefaedf27c1..e4770a80ab3 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java @@ -35,7 +35,6 @@ import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggableComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; -import org.apache.logging.log4j.core.config.builder.api.MonitorResourceComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; @@ -125,26 +124,6 @@ public PropertiesConfiguration build() { } } - Properties monitorResources = PropertiesUtil.extractSubset(rootProperties, "monitorResources"); - if (monitorResources.size() > 0) { - final String monitorResourcesType = (String) monitorResources.remove("type"); - if (!"MonitorResources".equals(monitorResourcesType)) { - throw new ConfigurationException( - "No or invalid type provided for monitorResouces - must be MonitorResources"); - } - final Map monitorResourceMap = - PropertiesUtil.partitionOnCommonPrefixes(monitorResources); - for (final Map.Entry entry : monitorResourceMap.entrySet()) { - final Properties monitorResourceProps = entry.getValue(); - final String monitorResourceType = (String) monitorResourceProps.remove("type"); - if (!"MonitorResource".equals(monitorResourceType)) { - throw new ConfigurationException( - "No or invalid type provided for monitorResouce - must be MonitorResource"); - } - builder.add(createMonitorResource(entry.getKey().trim(), entry.getValue())); - } - } - final String filterProp = rootProperties.getProperty("filters"); if (filterProp != null) { final String[] filterNames = filterProp.split(","); @@ -208,11 +187,30 @@ public PropertiesConfiguration build() { builder.add(createRootLogger(props)); } + processRemainingProperties(builder, rootProperties); + builder.setLoggerContext(loggerContext); return builder.build(false); } + private void processRemainingProperties( + final ConfigurationBuilder builder, final Properties properties) { + while (properties.size() > 0) { + final String propertyName = + properties.stringPropertyNames().iterator().next(); + final int index = propertyName.indexOf('.'); + if (index > 0) { + final String prefix = propertyName.substring(0, index); + final Properties componentProperties = PropertiesUtil.extractSubset(properties, prefix); + ComponentBuilder componentBuilder = createComponent(builder, prefix, componentProperties); + builder.addComponent(componentBuilder); + } else { + properties.remove(propertyName); + } + } + } + private ScriptComponentBuilder createScript(final Properties properties) { final String name = (String) properties.remove("name"); final String language = (String) properties.remove("language"); @@ -271,14 +269,6 @@ private AppenderRefComponentBuilder createAppenderRef(final String key, final Pr return addFiltersToComponent(appenderRefBuilder, properties); } - private MonitorResourceComponentBuilder createMonitorResource(final String key, final Properties properties) { - final String uri = (String) properties.remove("uri"); - if (Strings.isEmpty(uri)) { - throw new ConfigurationException("No uri attribute provided for MonitorResource " + key); - } - return builder.newMonitorResource(uri); - } - private LoggerComponentBuilder createLogger(final String key, final Properties properties) { final String levelAndRefs = properties.getProperty(""); final String name = (String) properties.remove(CONFIG_NAME); @@ -361,12 +351,17 @@ private LayoutComponentBuilder createLayout(final String appenderName, final Pro private static > ComponentBuilder createComponent( final ComponentBuilder parent, final String key, final Properties properties) { + return createComponent(parent.getBuilder(), key, properties); + } + + private static > ComponentBuilder createComponent( + final ConfigurationBuilder parentBuilder, final String key, final Properties properties) { final String name = (String) properties.remove(CONFIG_NAME); final String type = (String) properties.remove(CONFIG_TYPE); if (Strings.isEmpty(type)) { throw new ConfigurationException("No type attribute provided for component " + key); } - final ComponentBuilder componentBuilder = parent.getBuilder().newComponent(name, type); + final ComponentBuilder componentBuilder = parentBuilder.newComponent(name, type); return processRemainingProperties(componentBuilder, properties); } From aa6ceb336e2ea6dfed9d806846732f1b21c07a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 28 May 2025 16:30:13 +0200 Subject: [PATCH 08/12] Fix `monitor-resources.properties` --- .../examples/manual/configuration/monitor-resources.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties index 7546b197b04..dd772d9ff10 100644 --- a/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties +++ b/src/site/antora/modules/ROOT/examples/manual/configuration/monitor-resources.properties @@ -15,5 +15,8 @@ # limitations under the License. # monitorInterval = 30 +monitorResources.type = MonitorResources +monitorResources.0.type = MonitorResource monitorResources.0.uri = file://path/to/external-file-1.txt +monitorResources.1.type = MonitorResource monitorResources.1.uri = file://path/to/external-file-2.txt From 3878fb6532d06cc90aaaf0411cc676756ba737be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 28 May 2025 16:32:08 +0200 Subject: [PATCH 09/12] Rename `MonitorResource.Builder::withUri` to `setUri` --- .../org/apache/logging/log4j/core/config/MonitorResource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java index a9417a81bad..235e8645aa8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/MonitorResource.java @@ -41,13 +41,13 @@ public static Builder newBuilder() { /** * Builds MonitorResource instances. */ - public static class Builder implements org.apache.logging.log4j.core.util.Builder { + public static final class Builder implements org.apache.logging.log4j.core.util.Builder { @PluginBuilderAttribute @Required(message = "No URI provided") private URI uri; - public Builder withUri(final URI uri) { + public Builder setUri(final URI uri) { this.uri = uri; return this; } From 21fba0dd187877e641e80a6825520ab1b4da93f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 28 May 2025 16:38:17 +0200 Subject: [PATCH 10/12] Simplify `MonitorResourcesTest` --- .../logging/log4j/core/MonitorResourcesTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java index 923d1022c67..e139da9acba 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java @@ -53,14 +53,10 @@ void test_reconfiguration(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path final ConfigurationSource configSource = new ConfigurationSource(new Source(configFile), new byte[] {}, 0); final int monitorInterval = 3; - ComponentBuilder monitorResourcesComponent = configBuilder.newComponent("MonitorResources"); - monitorResourcesComponent.addComponent(monitorResourcesComponent - .getBuilder() - .newComponent("MonitorResource") + final ComponentBuilder monitorResourcesComponent = configBuilder.newComponent("MonitorResources"); + monitorResourcesComponent.addComponent(configBuilder.newComponent("MonitorResource") .addAttribute("uri", externalResourceFile1.toUri().toString())); - monitorResourcesComponent.addComponent(monitorResourcesComponent - .getBuilder() - .newComponent("MonitorResource") + monitorResourcesComponent.addComponent(configBuilder.newComponent("MonitorResource") .addAttribute("uri", externalResourceFile2.toUri().toString())); final Configuration config = configBuilder From 3d66a6b5a09f978f47ee09c7116db6959ef7d5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 28 May 2025 17:24:30 +0200 Subject: [PATCH 11/12] Fix Spotless failures --- .../org/apache/logging/log4j/core/MonitorResourcesTest.java | 6 ++++-- .../core/async/AsyncWaitStrategyFactoryConfigTest.java | 2 +- .../resources/AsyncWaitStrategyFactoryConfigTest.properties | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java index e139da9acba..4481ec62643 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/MonitorResourcesTest.java @@ -54,9 +54,11 @@ void test_reconfiguration(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path final int monitorInterval = 3; final ComponentBuilder monitorResourcesComponent = configBuilder.newComponent("MonitorResources"); - monitorResourcesComponent.addComponent(configBuilder.newComponent("MonitorResource") + monitorResourcesComponent.addComponent(configBuilder + .newComponent("MonitorResource") .addAttribute("uri", externalResourceFile1.toUri().toString())); - monitorResourcesComponent.addComponent(configBuilder.newComponent("MonitorResource") + monitorResourcesComponent.addComponent(configBuilder + .newComponent("MonitorResource") .addAttribute("uri", externalResourceFile2.toUri().toString())); final Configuration config = configBuilder diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java index 71710b08bfd..c1ede9cb447 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/async/AsyncWaitStrategyFactoryConfigTest.java @@ -44,7 +44,7 @@ void testConfigWaitStrategyFactory(final LoggerContext context) { "factory is YieldingWaitStrategyFactory", asyncWaitStrategyFactory instanceof YieldingWaitStrategyFactory); } - + @Test @LoggerContextSource("AsyncWaitStrategyFactoryConfigTest.properties") void testConfigWaitStrategyFactoryFromProperties(final LoggerContext context) { diff --git a/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties b/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties index 9414ba1b509..e8a76f6fda5 100644 --- a/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties +++ b/log4j-core-test/src/test/resources/AsyncWaitStrategyFactoryConfigTest.properties @@ -17,4 +17,3 @@ strategy.type = AsyncWaitStrategyFactory strategy.class = org.apache.logging.log4j.core.async.AsyncWaitStrategyFactoryConfigTest$YieldingWaitStrategyFactory - From ffcaf51098f1a829ec54cf67bf9ca5c94643afc9 Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Thu, 29 May 2025 18:52:30 +0100 Subject: [PATCH 12/12] Removed no longer needed classes Signed-off-by: MichaelMorris --- .../api/MonitorResourceComponentBuilder.java | 22 ------------- ...efaultMonitorResourceComponentBuilder.java | 31 ------------------- 2 files changed, 53 deletions(-) delete mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorResourceComponentBuilder.java delete mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorResourceComponentBuilder.java diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorResourceComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorResourceComponentBuilder.java deleted file mode 100644 index 39ad975344b..00000000000 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/MonitorResourceComponentBuilder.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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.core.config.builder.api; - -/** - * Assembler for constructing MonitorUri Components. - */ -public interface MonitorResourceComponentBuilder extends ComponentBuilder {} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorResourceComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorResourceComponentBuilder.java deleted file mode 100644 index d6942e66a9c..00000000000 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultMonitorResourceComponentBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.core.config.builder.impl; - -import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.builder.api.MonitorResourceComponentBuilder; - -class DefaultMonitorResourceComponentBuilder - extends DefaultComponentAndConfigurationBuilder - implements MonitorResourceComponentBuilder { - - public DefaultMonitorResourceComponentBuilder( - final DefaultConfigurationBuilder builder, final String uri) { - super(builder, "MonitorResource"); - addAttribute("uri", uri); - } -}