Skip to content

Commit ada292f

Browse files
Reworked to to make monitorUris a dedicated element instead of an attribute of the Configuration element
Signed-off-by: MichaelMorris <[email protected]>
1 parent 0308808 commit ada292f

26 files changed

+406
-191
lines changed

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

-24
Original file line numberDiff line numberDiff line change
@@ -544,30 +544,6 @@ 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-
571547
@Test
572548
void testSuppressedThrowable(final LoggerContext context) {
573549
final org.apache.logging.log4j.Logger testLogger = context.getLogger("org.apache.logging.log4j.nothrown");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.core;
18+
19+
import static org.awaitility.Awaitility.waitAtMost;
20+
21+
import java.io.IOException;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import java.util.Collections;
25+
import java.util.concurrent.TimeUnit;
26+
import org.apache.logging.log4j.core.config.Configuration;
27+
import org.apache.logging.log4j.core.config.ConfigurationSource;
28+
import org.apache.logging.log4j.core.config.Configurator;
29+
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
30+
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
31+
import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration;
32+
import org.apache.logging.log4j.core.util.Source;
33+
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.api.io.CleanupMode;
35+
import org.junit.jupiter.api.io.TempDir;
36+
37+
public class MonitorUriTest {
38+
39+
private static final int MONITOR_INTERVAL = 3;
40+
41+
@Test
42+
void testReconfigureOnChangeInMonitorUri(@TempDir(cleanup = CleanupMode.ON_SUCCESS) final Path tempDir)
43+
throws IOException {
44+
ConfigurationBuilder<PropertiesConfiguration> configBuilder =
45+
ConfigurationBuilderFactory.newConfigurationBuilder(PropertiesConfiguration.class);
46+
Path config = tempDir.resolve("config.xml");
47+
Path monitorUri = tempDir.resolve("monitorUri.xml");
48+
ConfigurationSource configSource = new ConfigurationSource(new Source(config), new byte[] {}, 0);
49+
Configuration configuration = configBuilder
50+
.setConfigurationSource(configSource)
51+
.setMonitorInterval(String.valueOf(MONITOR_INTERVAL))
52+
.add(configBuilder.newMonitorUri(monitorUri.toUri().toString()))
53+
.build();
54+
55+
try (LoggerContext loggerContext = Configurator.initialize(configuration)) {
56+
Files.write(monitorUri, Collections.singletonList("a change"));
57+
waitAtMost(MONITOR_INTERVAL + 2, TimeUnit.SECONDS)
58+
.until(() -> loggerContext.getConfiguration() != configuration);
59+
}
60+
}
61+
}

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

-21
Original file line numberDiff line numberDiff line change
@@ -64,25 +64,4 @@ 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-
}
8867
}

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

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

2322
property.filename = target/test-properties.log
2423

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

+1-1
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" monitorUris="target/test-classes/org/apache/logging/log4j/core/net/ssl/keyStore.p12">
18+
<Configuration status="OFF" name="XMLConfigTest" monitorInterval="1">
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

+36-14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.InputStream;
2323
import java.lang.ref.WeakReference;
2424
import java.net.URI;
25+
import java.net.URISyntaxException;
2526
import java.util.ArrayList;
2627
import java.util.Arrays;
2728
import java.util.Collection;
@@ -132,6 +133,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
132133
private ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap<>();
133134
private ConcurrentMap<String, LoggerConfig> loggerConfigs = new ConcurrentHashMap<>();
134135
private List<CustomLevelConfig> customLevels = Collections.emptyList();
136+
private List<URI> uris = Collections.emptyList();
135137
private final ConcurrentMap<String, String> propertyMap = new ConcurrentHashMap<>();
136138
private final Interpolator tempLookup = new Interpolator(propertyMap);
137139
private final StrSubstitutor runtimeStrSubstitutor = new RuntimeStrSubstitutor(tempLookup);
@@ -275,14 +277,6 @@ protected void initializeWatchers(
275277
final Reconfigurable reconfigurable,
276278
final ConfigurationSource configSource,
277279
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) {
286280
if (configSource != null && (configSource.getFile() != null || configSource.getURL() != null)) {
287281
if (monitorIntervalSeconds > 0) {
288282
watchManager.setIntervalSeconds(monitorIntervalSeconds);
@@ -292,7 +286,7 @@ protected void initializeWatchers(
292286
final long lastModified = file.lastModified();
293287
final ConfigurationFileWatcher watcher =
294288
new ConfigurationFileWatcher(this, reconfigurable, listeners, lastModified);
295-
watchManager.watch(cfgSource, auxiliarySources, watcher);
289+
watchManager.watch(cfgSource, watcher);
296290
} else if (configSource.getURL() != null) {
297291
monitorSource(reconfigurable, configSource);
298292
}
@@ -331,10 +325,19 @@ public void start() {
331325
LOGGER.info("Starting configuration {}...", this);
332326
this.setStarting();
333327
if (watchManager.getIntervalSeconds() >= 0) {
334-
LOGGER.info(
335-
"Start watching for changes to {} every {} seconds",
336-
getConfigurationSource(),
337-
watchManager.getIntervalSeconds());
328+
if (uris != null && uris.size() > 0) {
329+
LOGGER.info(
330+
"Start watching for changes to {} and {} every {} seconds",
331+
getConfigurationSource(),
332+
uris,
333+
watchManager.getIntervalSeconds());
334+
watchManager.addMonitorUris(configurationSource.getSource(), uris);
335+
} else {
336+
LOGGER.info(
337+
"Start watching for changes to {} every {} seconds",
338+
getConfigurationSource(),
339+
watchManager.getIntervalSeconds());
340+
}
338341
watchManager.start();
339342
}
340343
if (hasAsyncLoggers()) {
@@ -737,9 +740,16 @@ protected void doConfigure() {
737740
} else if (child.isInstanceOf(AsyncWaitStrategyFactoryConfig.class)) {
738741
final AsyncWaitStrategyFactoryConfig awsfc = child.getObject(AsyncWaitStrategyFactoryConfig.class);
739742
asyncWaitStrategyFactory = awsfc.createWaitStrategyFactory();
743+
} else if (child.isInstanceOf(MonitorUris.class)) {
744+
uris = convertToJavaNetUris(child.getObject(MonitorUris.class).getUris());
740745
} else {
741746
final List<String> expected = Arrays.asList(
742-
"\"Appenders\"", "\"Loggers\"", "\"Properties\"", "\"Scripts\"", "\"CustomLevels\"");
747+
"\"Appenders\"",
748+
"\"Loggers\"",
749+
"\"Properties\"",
750+
"\"Scripts\"",
751+
"\"CustomLevels\"",
752+
"\"MonitorUris\"");
743753
LOGGER.error(
744754
"Unknown object \"{}\" of type {} is ignored: try nesting it inside one of: {}.",
745755
child.getName(),
@@ -775,6 +785,18 @@ protected void doConfigure() {
775785
setParents();
776786
}
777787

788+
private List<URI> convertToJavaNetUris(final List<Uri> uris) {
789+
final List<URI> javaNetUris = new ArrayList<>();
790+
for (Uri uri : uris) {
791+
try {
792+
javaNetUris.add(new URI(uri.getUri()));
793+
} catch (URISyntaxException e) {
794+
LOGGER.error("Error parsing monitor URI: " + uri, e);
795+
}
796+
}
797+
return javaNetUris;
798+
}
799+
778800
public static Level getDefaultLevel() {
779801
final String levelName = PropertiesUtil.getProperties()
780802
.getStringProperty(DefaultConfiguration.DEFAULT_LEVEL, Level.ERROR.name());

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

+21-30
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package org.apache.logging.log4j.core.config;
1818

1919
import java.io.File;
20-
import java.util.Collection;
20+
import java.net.URI;
2121
import java.util.HashMap;
2222
import java.util.List;
2323
import java.util.Map;
@@ -31,66 +31,57 @@
3131
*/
3232
public class ConfigurationFileWatcher extends AbstractWatcher implements FileWatcher {
3333

34-
private File file;
35-
private long lastModifiedMillis;
36-
private Map<File, Long> auxiliaryFiles = new HashMap<>();
34+
private Map<File, Long> monitoredFiles = new HashMap<>();
3735

3836
public ConfigurationFileWatcher(
3937
final Configuration configuration,
4038
final Reconfigurable reconfigurable,
4139
final List<ConfigurationListener> configurationListeners,
4240
long lastModifiedMillis) {
4341
super(configuration, reconfigurable, configurationListeners);
44-
this.lastModifiedMillis = lastModifiedMillis;
4542
}
4643

4744
@Override
4845
public long getLastModified() {
49-
Long latestModifiedAuxFile = 0L;
50-
for (final File auxFile : auxiliaryFiles.keySet()) {
51-
if (auxFile.lastModified() > latestModifiedAuxFile) {
52-
latestModifiedAuxFile = auxFile.lastModified();
46+
Long lastModifiedMillis = 0L;
47+
for (final File monitoredFile : monitoredFiles.keySet()) {
48+
if (monitoredFile.lastModified() > lastModifiedMillis) {
49+
lastModifiedMillis = monitoredFile.lastModified();
5350
}
5451
}
55-
56-
return file != null
57-
? file.lastModified() > latestModifiedAuxFile ? file.lastModified() : latestModifiedAuxFile
58-
: 0;
52+
return lastModifiedMillis;
5953
}
6054

6155
@Override
6256
public void fileModified(final File file) {
63-
lastModifiedMillis = file.lastModified();
64-
auxiliaryFiles.entrySet().stream()
65-
.forEach(auxFile -> auxFile.setValue(auxFile.getKey().lastModified()));
57+
monitoredFiles.entrySet().stream()
58+
.forEach(monitoredFile ->
59+
monitoredFile.setValue(monitoredFile.getKey().lastModified()));
6660
}
6761

6862
@Override
6963
public void watching(final Source source) {
70-
file = source.getFile();
71-
lastModifiedMillis = file.lastModified();
64+
File file = source.getFile();
65+
monitoredFiles.put(file, file.lastModified());
7266
super.watching(source);
7367
}
7468

7569
/**
76-
* Called when the Watcher is registered.
77-
* @param source the Source that is being watched.
78-
* @param auxiliarySources auxiliary sources also being watched.
70+
* Add the given URIs to be watched.
71+
*
72+
* @param monitorUris URIs to also watch
7973
*/
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());
74+
public void addMonitorUris(final List<URI> monitorUris) {
75+
monitorUris.forEach(uri -> {
76+
File additionalFile = new Source(uri).getFile();
77+
monitoredFiles.put(additionalFile, additionalFile.lastModified());
8578
});
86-
super.watching(source);
8779
}
8880

8981
@Override
9082
public boolean isModified() {
91-
return lastModifiedMillis != file.lastModified()
92-
|| auxiliaryFiles.entrySet().stream()
93-
.anyMatch(file -> file.getValue() != file.getKey().lastModified());
83+
return monitoredFiles.entrySet().stream()
84+
.anyMatch(file -> file.getValue() != file.getKey().lastModified());
9485
}
9586

9687
@Override

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

+4
Original file line numberDiff line numberDiff line change
@@ -382,4 +382,8 @@ public String toString() {
382382
return null;
383383
}
384384
}
385+
386+
Source getSource() {
387+
return this.source;
388+
}
385389
}

0 commit comments

Comments
 (0)