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>