This is an automated email from the ASF dual-hosted git repository. rgoers pushed a commit to branch release-2.x in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 7a9a466a756a8bd77bc4bc29ee9517c4c098f470 Author: Ralph Goers <[email protected]> AuthorDate: Wed Jan 30 17:02:50 2019 -0700 LOG4J2-913 - Add support for dynamic reconfiguration --- .../log4j/core/config/AbstractConfiguration.java | 48 ++++++++++++++-------- .../logging/log4j/core/config/HttpWatcher.java | 2 +- .../logging/log4j/core/util/WatchEventService.java | 28 +++++++++++++ .../logging/log4j/core/util/WatchManager.java | 43 +++++++++++++++++++ .../apache/logging/log4j/core/util/Watcher.java | 1 + .../log4j-spring-cloud-config-client/pom.xml | 8 ++++ .../cloud/config/client/Log4j2EventListener.java | 45 ++++++++++++++++++++ .../cloud/config/client/WatchEventManager.java | 48 ++++++++++++++++++++++ ...pache.logging.log4j.core.util.WatchEventService | 1 + .../src/main/resources/META-INF/spring.factories | 1 + .../src/main/resources/log4j2.component.properties | 0 log4j-spring-cloud-config/pom.xml | 10 +++++ 12 files changed, 216 insertions(+), 19 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 de0d6eb..4a93e02 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 @@ -245,27 +245,39 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement protected void initializeWatchers(Reconfigurable reconfigurable, ConfigurationSource configSource, int monitorIntervalSeconds) { - if ((configSource.getFile() != null || configSource.getURL() != null) && monitorIntervalSeconds > 0) { - watchManager.setIntervalSeconds(monitorIntervalSeconds); - if (configSource.getFile() != null) { - final Source cfgSource = new Source(configSource); - final long lastModifeid = configSource.getFile().lastModified(); - final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher(this, reconfigurable, - listeners, lastModifeid); - watchManager.watch(cfgSource, watcher); - } else if (configSource.getURL() != null) { - if (configSource.getLastModified() > 0) { - final Source cfgSource = new Source(configSource); - final Watcher watcher = WatcherFactory.getInstance(pluginPackages) - .newWatcher(cfgSource, this, reconfigurable, listeners, configSource.getLastModified()); - watchManager.watch(cfgSource, watcher); - } else { - LOGGER.info("{} does not support dynamic reconfiguration", configSource.getURI()); - } - } + if (configSource.getFile() != null || configSource.getURL() != null) { + if (monitorIntervalSeconds > 0) { + watchManager.setIntervalSeconds(monitorIntervalSeconds); + if (configSource.getFile() != null) { + final Source cfgSource = new Source(configSource); + final long lastModifeid = configSource.getFile().lastModified(); + final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher(this, reconfigurable, + listeners, lastModifeid); + watchManager.watch(cfgSource, watcher); + } else { + if (configSource.getURL() != null) { + monitorSource(reconfigurable, configSource); + } + } + } else if (watchManager.hasEventListeners() && configSource.getURL() != null && monitorIntervalSeconds >= 0) { + monitorSource(reconfigurable, configSource); + } } } + private void monitorSource(Reconfigurable reconfigurable, ConfigurationSource configSource) { + if (configSource.getLastModified() > 0) { + final Source cfgSource = new Source(configSource); + final Watcher watcher = WatcherFactory.getInstance(pluginPackages) + .newWatcher(cfgSource, this, reconfigurable, listeners, configSource.getLastModified()); + if (watcher != null) { + watchManager.watch(cfgSource, watcher); + } + } else { + LOGGER.info("{} does not support dynamic reconfiguration", configSource.getURI()); + } + } + /** * Start the configuration. */ diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java index a2d1220..6072168 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/HttpWatcher.java @@ -30,7 +30,7 @@ import org.apache.logging.log4j.util.PropertiesUtil; /** * */ -@Plugin(name = "http", category = Watcher.CATEGORY, printObject = true) +@Plugin(name = "http", category = Watcher.CATEGORY, elementType = Watcher.ELEMENT_TYPE, printObject = true) @PluginAliases("https") public class HttpWatcher extends AbstractWatcher { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchEventService.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchEventService.java new file mode 100644 index 0000000..31021c8 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchEventService.java @@ -0,0 +1,28 @@ +/* + * 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.util; + +/** + * + */ +public interface WatchEventService { + + void subscribe(WatchManager manager); + + void unsubscribe(WatchManager manager); + +} 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 37e0a44..3e37152 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 @@ -17,9 +17,13 @@ package org.apache.logging.log4j.core.util; import java.io.File; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.ServiceLoader; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledFuture; @@ -30,6 +34,7 @@ import org.apache.logging.log4j.core.AbstractLifeCycle; import org.apache.logging.log4j.core.config.ConfigurationFileWatcher; import org.apache.logging.log4j.core.config.ConfigurationScheduler; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; /** * Manages {@link FileWatcher}s. @@ -44,11 +49,22 @@ public class WatchManager extends AbstractLifeCycle { private int intervalSeconds = 0; private ScheduledFuture<?> future; private final ConfigurationScheduler scheduler; + private final List<WatchEventService> eventServiceList; + private final UUID id = UuidUtil.getTimeBasedUuid(); public WatchManager(final ConfigurationScheduler scheduler) { this.scheduler = scheduler; + eventServiceList = getEventServices(); } + public UUID getId() { + return this.id; + } + + public boolean hasEventListeners() { + return eventServiceList.size() > 0; + } + /** * Resets all file monitors to their current last modified time. If this manager does not watch any file, nothing * happens. @@ -141,15 +157,22 @@ public class WatchManager extends AbstractLifeCycle { @Override public void start() { super.start(); + if (intervalSeconds > 0) { future = scheduler.scheduleWithFixedDelay(new WatchRunnable(), intervalSeconds, intervalSeconds, TimeUnit.SECONDS); } + for (WatchEventService service : eventServiceList) { + service.subscribe(this); + } } @Override public boolean stop(final long timeout, final TimeUnit timeUnit) { setStopping(); + for (WatchEventService service : eventServiceList) { + service.unsubscribe(this); + } final boolean stopped = stop(future); setStopped(); return stopped; @@ -179,6 +202,10 @@ public class WatchManager extends AbstractLifeCycle { watchers.remove(source); } + public void checkFiles() { + new WatchRunnable().run(); + } + /** * Watches the given file. * @@ -248,6 +275,22 @@ public class WatchManager extends AbstractLifeCycle { return new Date(millis).toString(); } + private List<WatchEventService> getEventServices() { + List<WatchEventService> list = new ArrayList<>(); + for (final ClassLoader classLoader : LoaderUtil.getClassLoaders()) { + try { + final ServiceLoader<WatchEventService > serviceLoader = + ServiceLoader.load(WatchEventService.class, classLoader); + for (final WatchEventService service : serviceLoader) { + list.add(service); + } + } catch (final Throwable ex) { + LOGGER.debug("Unable to retrieve WatchEventService from ClassLoader {}", classLoader, ex); + } + } + return list; + } + private final class WatchRunnable implements Runnable { // Use a hard class reference here in case a refactoring changes the class name. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java index 7ae9cd0..c2be48d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Watcher.java @@ -30,6 +30,7 @@ import org.apache.logging.log4j.core.config.Reconfigurable; public interface Watcher { public static final String CATEGORY = "Watcher"; + public static final String ELEMENT_TYPE = "watcher"; /** * Returns the list of listeners for this configuration. diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml index 8799078..c0a31f7 100644 --- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml @@ -48,6 +48,14 @@ <artifactId>spring-boot</artifactId> </dependency> <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <scope>test</scope> diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java new file mode 100644 index 0000000..29cdc92 --- /dev/null +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/Log4j2EventListener.java @@ -0,0 +1,45 @@ +/* + * 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.spring.cloud.config.client; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.cloud.context.refresh.ContextRefresher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * Listen for events indicating the remote configuration has changed. + */ +@Component +@ConditionalOnClass(ContextRefresher.class) +@ConditionalOnBean(ContextRefresher.class) +@ConditionalOnProperty(value = "spring.cloud.config.watch.enabled") +public class Log4j2EventListener { + + private static Logger LOGGER = LogManager.getLogger(Log4j2EventListener.class); + + @EventListener(EnvironmentChangeEvent.class) + public void handleEnvironmentChangeEvent(EnvironmentChangeEvent event) { + LOGGER.debug("Environment change event received"); + WatchEventManager.publishEvent(); + } +} diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/WatchEventManager.java b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/WatchEventManager.java new file mode 100644 index 0000000..a49d5f2 --- /dev/null +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/java/org/apache/logging/log4j/spring/cloud/config/client/WatchEventManager.java @@ -0,0 +1,48 @@ +/* + * 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.spring.cloud.config.client; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.logging.log4j.core.util.WatchEventService; +import org.apache.logging.log4j.core.util.WatchManager; + +/** + * + */ +public class WatchEventManager implements WatchEventService { + private static final ConcurrentMap<UUID, WatchManager> watchManagers = new ConcurrentHashMap<>(); + + public static void publishEvent() { + for (WatchManager manager : watchManagers.values()) { + manager.checkFiles(); + } + } + + @Override + public void subscribe(WatchManager manager) { + watchManagers.put(manager.getId(), manager); + + } + + @Override + public void unsubscribe(WatchManager manager) { + watchManagers.remove(manager.getId()); + } +} diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/services/org.apache.logging.log4j.core.util.WatchEventService b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/services/org.apache.logging.log4j.core.util.WatchEventService new file mode 100644 index 0000000..701bfd3 --- /dev/null +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/services/org.apache.logging.log4j.core.util.WatchEventService @@ -0,0 +1 @@ +org.apache.logging.log4j.spring.cloud.config.client.WatchEventManager \ No newline at end of file diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/spring.factories b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..95a972f --- /dev/null +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apache.logging.log4j.spring.cloud.config.client.Log4j2EventListener \ No newline at end of file diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/log4j2.component.properties b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/src/main/resources/log4j2.component.properties new file mode 100644 index 0000000..e69de29 diff --git a/log4j-spring-cloud-config/pom.xml b/log4j-spring-cloud-config/pom.xml index a736e86..37282c9 100644 --- a/log4j-spring-cloud-config/pom.xml +++ b/log4j-spring-cloud-config/pom.xml @@ -53,6 +53,16 @@ </dependency> <dependency> <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <version>${spring.version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + <version>${spring.version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency>
