Skip to content

Commit 0308808

Browse files
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 <[email protected]>
1 parent 6f4fab9 commit 0308808

22 files changed

+197
-17
lines changed

log4j-core-test/src/test/java/org/apache/logging/log4j/core/LoggerTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,30 @@ void testReconfiguration(final LoggerContext context) throws Exception {
544544
assertNotSame(newConfig, oldConfig, "Reconfiguration failed");
545545
}
546546

547+
@Test
548+
void testReconfigurationMonitorUris(final LoggerContext context) throws Exception {
549+
final Configuration oldConfig = context.getConfiguration();
550+
final int MONITOR_INTERVAL_SECONDS = 5;
551+
final File file = new File("target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12");
552+
final long orig = file.lastModified();
553+
final long newTime = orig + 10000;
554+
assertTrue(file.setLastModified(newTime), "setLastModified should have succeeded.");
555+
TimeUnit.SECONDS.sleep(MONITOR_INTERVAL_SECONDS + 1);
556+
for (int i = 0; i < 17; ++i) {
557+
logger.debug("Reconfigure");
558+
}
559+
Thread.sleep(100);
560+
for (int i = 0; i < 20; i++) {
561+
if (context.getConfiguration() != oldConfig) {
562+
break;
563+
}
564+
Thread.sleep(50);
565+
}
566+
final Configuration newConfig = context.getConfiguration();
567+
assertNotNull(newConfig, "No configuration");
568+
assertNotSame(newConfig, oldConfig, "Reconfiguration failed");
569+
}
570+
547571
@Test
548572
void testSuppressedThrowable(final LoggerContext context) {
549573
final org.apache.logging.log4j.Logger testLogger = context.getLogger("org.apache.logging.log4j.nothrown");

log4j-core-test/src/test/java/org/apache/logging/log4j/core/PropertiesFileConfigTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,25 @@ void testReconfiguration(final LoggerContext context) throws Exception {
6464
} while (newConfig == oldConfig && loopCount++ < 5);
6565
assertNotSame(newConfig, oldConfig, "Reconfiguration failed");
6666
}
67+
68+
@Test
69+
void testReconfigurationMonitorUris(final LoggerContext context) throws Exception {
70+
final Configuration oldConfig = context.getConfiguration();
71+
final int MONITOR_INTERVAL_SECONDS = 5;
72+
final File file = new File("target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12");
73+
final long orig = file.lastModified();
74+
final long newTime = orig + 10000;
75+
assertTrue(file.setLastModified(newTime), "setLastModified should have succeeded.");
76+
TimeUnit.SECONDS.sleep(MONITOR_INTERVAL_SECONDS + 1);
77+
for (int i = 0; i < 17; ++i) {
78+
logger.info("Reconfigure");
79+
}
80+
int loopCount = 0;
81+
Configuration newConfig;
82+
do {
83+
Thread.sleep(100);
84+
newConfig = context.getConfiguration();
85+
} while (newConfig == oldConfig && loopCount++ < 5);
86+
assertNotSame(newConfig, oldConfig, "Reconfiguration failed");
87+
}
6788
}

log4j-core-test/src/test/resources/log4j-test2.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
status = debug
1919
name = PropertiesConfigTest
2020
monitorInterval = 1
21+
monitorUris = target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12
2122

2223
property.filename = target/test-properties.log
2324

log4j-core-test/src/test/resources/log4j-test2.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
~ See the License for the specific language governing permissions and
1616
~ limitations under the License.
1717
-->
18-
<Configuration status="OFF" name="XMLConfigTest" monitorInterval="1">
18+
<Configuration status="OFF" name="XMLConfigTest" monitorInterval="1" monitorUris="target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12">
1919
<Appenders>
2020
<RollingFile name="HostFile" fileName="target/${hostName}.log" filePattern="target/${hostName}-%d{MM-dd-yyyy}-%i.log">
2121
<PatternLayout>

log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,14 @@ protected void initializeWatchers(
275275
final Reconfigurable reconfigurable,
276276
final ConfigurationSource configSource,
277277
final int monitorIntervalSeconds) {
278+
initializeWatchers(reconfigurable, configSource, Collections.emptySet(), monitorIntervalSeconds);
279+
}
280+
281+
protected void initializeWatchers(
282+
final Reconfigurable reconfigurable,
283+
final ConfigurationSource configSource,
284+
final Collection<Source> auxiliarySources,
285+
final int monitorIntervalSeconds) {
278286
if (configSource != null && (configSource.getFile() != null || configSource.getURL() != null)) {
279287
if (monitorIntervalSeconds > 0) {
280288
watchManager.setIntervalSeconds(monitorIntervalSeconds);
@@ -284,7 +292,7 @@ protected void initializeWatchers(
284292
final long lastModified = file.lastModified();
285293
final ConfigurationFileWatcher watcher =
286294
new ConfigurationFileWatcher(this, reconfigurable, listeners, lastModified);
287-
watchManager.watch(cfgSource, watcher);
295+
watchManager.watch(cfgSource, auxiliarySources, watcher);
288296
} else if (configSource.getURL() != null) {
289297
monitorSource(reconfigurable, configSource);
290298
}

log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFileWatcher.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
package org.apache.logging.log4j.core.config;
1818

1919
import java.io.File;
20+
import java.util.Collection;
21+
import java.util.HashMap;
2022
import java.util.List;
23+
import java.util.Map;
2124
import org.apache.logging.log4j.core.util.AbstractWatcher;
2225
import org.apache.logging.log4j.core.util.FileWatcher;
2326
import org.apache.logging.log4j.core.util.Source;
@@ -30,6 +33,7 @@ public class ConfigurationFileWatcher extends AbstractWatcher implements FileWat
3033

3134
private File file;
3235
private long lastModifiedMillis;
36+
private Map<File, Long> auxiliaryFiles = new HashMap<>();
3337

3438
public ConfigurationFileWatcher(
3539
final Configuration configuration,
@@ -42,12 +46,23 @@ public ConfigurationFileWatcher(
4246

4347
@Override
4448
public long getLastModified() {
45-
return file != null ? file.lastModified() : 0;
49+
Long latestModifiedAuxFile = 0L;
50+
for (final File auxFile : auxiliaryFiles.keySet()) {
51+
if (auxFile.lastModified() > latestModifiedAuxFile) {
52+
latestModifiedAuxFile = auxFile.lastModified();
53+
}
54+
}
55+
56+
return file != null
57+
? file.lastModified() > latestModifiedAuxFile ? file.lastModified() : latestModifiedAuxFile
58+
: 0;
4659
}
4760

4861
@Override
4962
public void fileModified(final File file) {
5063
lastModifiedMillis = file.lastModified();
64+
auxiliaryFiles.entrySet().stream()
65+
.forEach(auxFile -> auxFile.setValue(auxFile.getKey().lastModified()));
5166
}
5267

5368
@Override
@@ -57,9 +72,25 @@ public void watching(final Source source) {
5772
super.watching(source);
5873
}
5974

75+
/**
76+
* Called when the Watcher is registered.
77+
* @param source the Source that is being watched.
78+
* @param auxiliarySources auxiliary sources also being watched.
79+
*/
80+
public void watching(final Source source, final Collection<Source> auxiliarySources) {
81+
file = source.getFile();
82+
lastModifiedMillis = file.lastModified();
83+
auxiliarySources.forEach(auxSource -> {
84+
auxiliaryFiles.put(auxSource.getFile(), auxSource.getFile().lastModified());
85+
});
86+
super.watching(source);
87+
}
88+
6089
@Override
6190
public boolean isModified() {
62-
return lastModifiedMillis != file.lastModified();
91+
return lastModifiedMillis != file.lastModified()
92+
|| auxiliaryFiles.entrySet().stream()
93+
.anyMatch(file -> file.getValue() != file.getKey().lastModified());
6394
}
6495

6596
@Override

log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,15 @@ public interface ConfigurationBuilder<T extends Configuration> extends Builder<T
418418
*/
419419
ConfigurationBuilder<T> setMonitorInterval(String intervalSeconds);
420420

421+
/**
422+
* Set the URIs to be monitored in addition to the configuration file
423+
* @param monitorUris the URIs to monitor
424+
* @return this builder instance
425+
*/
426+
default ConfigurationBuilder<T> setMonitorUris(final String monitorUris) {
427+
return this;
428+
}
429+
421430
/**
422431
* Sets the list of packages to search for plugins.
423432
* @param packages The comma separated list of packages.

log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
* @since 2.4
2121
*/
2222
@Export
23-
@Version("2.20.1")
23+
@Version("2.21.0")
2424
package org.apache.logging.log4j.core.config.builder.api;
2525

2626
import org.osgi.annotation.bundle.Export;

log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818

1919
import java.io.IOException;
2020
import java.io.InputStream;
21+
import java.net.URI;
22+
import java.net.URISyntaxException;
2123
import java.util.Arrays;
24+
import java.util.Collection;
25+
import java.util.HashSet;
2226
import java.util.List;
2327
import org.apache.logging.log4j.core.LoggerContext;
2428
import org.apache.logging.log4j.core.config.AbstractConfiguration;
@@ -30,6 +34,8 @@
3034
import org.apache.logging.log4j.core.config.plugins.util.PluginType;
3135
import org.apache.logging.log4j.core.config.status.StatusConfiguration;
3236
import org.apache.logging.log4j.core.util.Patterns;
37+
import org.apache.logging.log4j.core.util.Source;
38+
import org.apache.logging.log4j.util.Strings;
3339

3440
/**
3541
* 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) {
148154
}
149155

150156
public void setMonitorInterval(final int intervalSeconds) {
157+
initializeMonitoring(intervalSeconds, "");
158+
}
159+
160+
public void initializeMonitoring(final int intervalSeconds, final String monitorUris) {
151161
if (this instanceof Reconfigurable && intervalSeconds > 0) {
152-
initializeWatchers((Reconfigurable) this, getConfigurationSource(), intervalSeconds);
162+
initializeWatchers(
163+
(Reconfigurable) this, getConfigurationSource(), getAuxiliarySources(monitorUris), intervalSeconds);
164+
}
165+
}
166+
167+
private Collection<Source> getAuxiliarySources(final String monitorUris) {
168+
final Collection<Source> auxiliarySources = new HashSet<>();
169+
if (Strings.isNotBlank(monitorUris)) {
170+
for (final String uri : Arrays.asList(monitorUris.split(Patterns.COMMA_SEPARATOR))) {
171+
try {
172+
auxiliarySources.add(new Source(new URI(uri)));
173+
} catch (URISyntaxException e) {
174+
LOGGER.error("Error parsing monitorUris: " + monitorUris, e);
175+
}
176+
}
153177
}
178+
return auxiliarySources;
154179
}
155180

156181
@Override

log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder;
6262
import org.apache.logging.log4j.core.util.Integers;
6363
import org.apache.logging.log4j.core.util.Throwables;
64+
import org.apache.logging.log4j.util.Strings;
6465

6566
/**
6667
* @param <T> The BuiltConfiguration type.
@@ -80,6 +81,7 @@ public class DefaultConfigurationBuilder<T extends BuiltConfiguration> implement
8081
private final Class<T> clazz;
8182
private ConfigurationSource source;
8283
private int monitorInterval;
84+
private String monitorUris;
8385
private Level level;
8486
private String destination;
8587
private String packages;
@@ -214,7 +216,7 @@ public T build(final boolean initialize) {
214216
if (advertiser != null) {
215217
configuration.createAdvertiser(advertiser, source);
216218
}
217-
configuration.setMonitorInterval(monitorInterval);
219+
configuration.initializeMonitoring(monitorInterval, monitorUris);
218220
} catch (final Exception ex) {
219221
throw new IllegalArgumentException("Invalid Configuration class specified", ex);
220222
}
@@ -287,6 +289,9 @@ private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLSt
287289
if (monitorInterval > 0) {
288290
xmlWriter.writeAttribute("monitorInterval", String.valueOf(monitorInterval));
289291
}
292+
if (Strings.isNotBlank(monitorUris)) {
293+
xmlWriter.writeAttribute("monitorUris", monitorUris);
294+
}
290295

291296
writeXmlSection(xmlWriter, properties);
292297
writeXmlSection(xmlWriter, scripts);
@@ -565,6 +570,12 @@ public ConfigurationBuilder<T> setMonitorInterval(final String intervalSeconds)
565570
return this;
566571
}
567572

573+
@Override
574+
public ConfigurationBuilder<T> setMonitorUris(final String monitorUris) {
575+
this.monitorUris = monitorUris;
576+
return this;
577+
}
578+
568579
@Override
569580
public ConfigurationBuilder<T> setPackages(final String packages) {
570581
this.packages = packages;

0 commit comments

Comments
 (0)