You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2019/05/05 05:27:27 UTC
[logging-log4j2] 06/12: LOG4J2-913 - Add support for dynamic
reconfiguration
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 <rg...@apache.org>
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>