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/07/29 02:08:27 UTC
[logging-log4j2] branch release-2.x updated: LOG4J2-2366 - Remove
references to LoggerContext when it is shutdown
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
The following commit(s) were added to refs/heads/release-2.x by this push:
new 4ac441d LOG4J2-2366 - Remove references to LoggerContext when it is shutdown
4ac441d is described below
commit 4ac441d1f9e4373c5477cc896057469b41b8b1f4
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Jul 28 19:07:26 2019 -0700
LOG4J2-2366 - Remove references to LoggerContext when it is shutdown
---
.../logging/log4j/spi/AbstractLoggerAdapter.java | 21 +++++-
.../log4j/spi/LoggerContextShutdownAware.java | 26 ++++++++
.../log4j/spi/LoggerContextShutdownEnabled.java | 29 ++++++++
.../logging/log4j/spi/LoggerAdapterTest.java | 77 +++++++++++++++++++---
.../apache/logging/log4j/core/LoggerContext.java | 33 +++++++++-
.../core/selector/ClassLoaderContextSelector.java | 10 ++-
.../apache/logging/slf4j/LoggerContextTest.java | 44 +++++++++++++
.../apache/logging/slf4j/LoggerContextTest.java | 44 +++++++++++++
src/changes/changes.xml | 3 +
9 files changed, 275 insertions(+), 12 deletions(-)
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLoggerAdapter.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLoggerAdapter.java
index e86f990..a850958 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLoggerAdapter.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLoggerAdapter.java
@@ -16,7 +16,9 @@
*/
package org.apache.logging.log4j.spi;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -32,12 +34,12 @@ import org.apache.logging.log4j.util.LoaderUtil;
* @param <L> the Logger class to adapt
* @since 2.1
*/
-public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L> {
+public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L>, LoggerContextShutdownAware {
/**
* A map to store loggers for their given LoggerContexts.
*/
- protected final Map<LoggerContext, ConcurrentMap<String, L>> registry = new WeakHashMap<>();
+ protected final Map<LoggerContext, ConcurrentMap<String, L>> registry = new ConcurrentHashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock (true);
@@ -53,6 +55,11 @@ public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L> {
return loggers.get(name);
}
+ @Override
+ public void contextShutdown(LoggerContext loggerContext) {
+ registry.remove(loggerContext);
+ }
+
/**
* Gets or creates the ConcurrentMap of named loggers for a given LoggerContext.
*
@@ -77,6 +84,9 @@ public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L> {
if (loggers == null) {
loggers = new ConcurrentHashMap<> ();
registry.put (context, loggers);
+ if (context instanceof LoggerContextShutdownEnabled) {
+ ((LoggerContextShutdownEnabled) context).addShutdownListener(this);
+ }
}
return loggers;
} finally {
@@ -85,6 +95,13 @@ public abstract class AbstractLoggerAdapter<L> implements LoggerAdapter<L> {
}
/**
+ * For unit testing. Consider to be private.
+ */
+ public Set<LoggerContext> getLoggerContexts() {
+ return new HashSet<>(registry.keySet());
+ }
+
+ /**
* Creates a new named logger for a given {@link LoggerContext}.
*
* @param name the name of the logger to create
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextShutdownAware.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextShutdownAware.java
new file mode 100644
index 0000000..d31c583
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextShutdownAware.java
@@ -0,0 +1,26 @@
+/*
+ * 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.spi;
+
+/**
+ * Interface allowing interested classes to know when a LoggerContext has shutdown - if the LoggerContext
+ * implementation provides a way to register listeners.
+ */
+public interface LoggerContextShutdownAware {
+
+ void contextShutdown(LoggerContext loggerContext);
+}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextShutdownEnabled.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextShutdownEnabled.java
new file mode 100644
index 0000000..f3d59c1
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContextShutdownEnabled.java
@@ -0,0 +1,29 @@
+/*
+ * 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.spi;
+
+import java.util.List;
+
+/**
+ * LoggerContexts implementing this are able register LoggerContextShutdownAware classes.
+ */
+public interface LoggerContextShutdownEnabled {
+
+ void addShutdownListener(LoggerContextShutdownAware listener);
+
+ List<LoggerContextShutdownAware> getListeners();
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/spi/LoggerAdapterTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/spi/LoggerAdapterTest.java
index 8a56b40..b2beb1d 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/spi/LoggerAdapterTest.java
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/spi/LoggerAdapterTest.java
@@ -16,12 +16,18 @@
*/
package org.apache.logging.log4j.spi;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.TestLogger;
+import org.apache.logging.log4j.TestLoggerContext;
+import org.apache.logging.log4j.TestLoggerContextFactory;
import org.apache.logging.log4j.simple.SimpleLoggerContext;
import org.junit.Test;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
-import java.util.logging.Logger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
@@ -68,12 +74,6 @@ public class LoggerAdapterTest {
}
- private static class TestLogger extends Logger {
- public TestLogger() {
- super("test", null);
- }
- }
-
private static class TestLoggerAdapter extends AbstractLoggerAdapter<Logger> {
@Override
@@ -87,6 +87,67 @@ public class LoggerAdapterTest {
}
}
+ private static class TestLoggerAdapter2 extends AbstractLoggerAdapter<Logger> {
+
+ @Override
+ protected Logger newLogger(String name, LoggerContext context) {
+ return context.getLogger(name);
+ }
+
+ @Override
+ protected LoggerContext getContext() {
+ return null;
+ }
+
+ public LoggerContext getContext(String fqcn) {
+ for (LoggerContext lc : registry.keySet()) {
+ TestLoggerContext2 context = (TestLoggerContext2) lc;
+ if (fqcn.equals(context.getName())) {
+ return context;
+ }
+ }
+ LoggerContext lc = new TestLoggerContext2(fqcn, this);
+ registry.put(lc, new ConcurrentHashMap<String, Logger>());
+ return lc;
+ }
+ }
+
+ private static class TestLoggerContext2 extends TestLoggerContext {
+ private final String name;
+ private final LoggerContextShutdownAware listener;
+
+ public TestLoggerContext2(String name, LoggerContextShutdownAware listener) {
+ this.name = name;
+ this.listener = listener;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void shutdown() {
+ listener.contextShutdown(this);
+ }
+ }
+
+ @Test
+ public void testCleanup() throws Exception {
+ final LoggerContextFactory factory = new TestLoggerContextFactory();
+ final TestLoggerAdapter2 adapter = new TestLoggerAdapter2();
+ for (int i = 0; i < 5; ++i) {
+ LoggerContext lc = adapter.getContext(Integer.toString(i));
+ lc.getLogger(Integer.toString(i));
+ }
+ assertEquals("Expected 5 LoggerContexts", 5, adapter.registry.size());
+ Set<LoggerContext> contexts = new HashSet<>(adapter.registry.keySet());
+ for (LoggerContext context : contexts) {
+ ((TestLoggerContext2) context).shutdown();
+ }
+ assertEquals("Expected 0 LoggerContexts", 0, adapter.registry.size());
+ }
+
+
+
/**
* Testing synchronization in the getLoggersInContext() method
*/
@@ -123,4 +184,4 @@ public class LoggerAdapterTest {
assertEquals(2, resultMap1.size());
}
}
-}
\ No newline at end of file
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index ddb5289..447e915 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -22,7 +22,10 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.URI;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -48,6 +51,8 @@ import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
import org.apache.logging.log4j.message.MessageFactory;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
+import org.apache.logging.log4j.spi.LoggerContextShutdownEnabled;
import org.apache.logging.log4j.spi.LoggerRegistry;
import org.apache.logging.log4j.spi.Terminable;
import org.apache.logging.log4j.spi.ThreadContextMapFactory;
@@ -60,7 +65,8 @@ import org.apache.logging.log4j.util.PropertiesUtil;
* filters, etc and will be atomically updated whenever a reconfigure occurs.
*/
public class LoggerContext extends AbstractLifeCycle
- implements org.apache.logging.log4j.spi.LoggerContext, AutoCloseable, Terminable, ConfigurationListener {
+ implements org.apache.logging.log4j.spi.LoggerContext, AutoCloseable, Terminable, ConfigurationListener,
+ LoggerContextShutdownEnabled {
static {
try {
@@ -80,6 +86,7 @@ public class LoggerContext extends AbstractLifeCycle
private final LoggerRegistry<Logger> loggerRegistry = new LoggerRegistry<>();
private final CopyOnWriteArrayList<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<>();
+ private volatile List<LoggerContextShutdownAware> listeners = null;
/**
* The Configuration is volatile to guarantee that initialization of the Configuration has completed before the
@@ -149,6 +156,21 @@ public class LoggerContext extends AbstractLifeCycle
}
}
+ public void addShutdownListener(LoggerContextShutdownAware listener) {
+ if (listeners == null) {
+ synchronized(this) {
+ if (listeners == null) {
+ listeners = Collections.synchronizedList(new ArrayList<LoggerContextShutdownAware>());
+ }
+ }
+ }
+ listeners.add(listener);
+ }
+
+ public List<LoggerContextShutdownAware> getListeners() {
+ return listeners;
+ }
+
/**
* Returns the current LoggerContext.
* <p>
@@ -360,6 +382,15 @@ public class LoggerContext extends AbstractLifeCycle
configLock.unlock();
this.setStopped();
}
+ if (listeners != null) {
+ for (LoggerContextShutdownAware listener : listeners) {
+ try {
+ listener.contextShutdown(this);
+ } catch (Exception ex) {
+ // Ignore the exception.
+ }
+ }
+ }
LOGGER.debug("Stopped LoggerContext[name={}, {}] with status {}", getName(), this, true);
return true;
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
index 8ffe5a5..a581efb 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.java
@@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.impl.ContextAnchor;
+import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.StackLocatorUtil;
@@ -43,7 +44,7 @@ import org.apache.logging.log4j.util.StackLocatorUtil;
*
* This ContextSelector should not be used with a Servlet Filter such as the Log4jServletFilter.
*/
-public class ClassLoaderContextSelector implements ContextSelector {
+public class ClassLoaderContextSelector implements ContextSelector, LoggerContextShutdownAware {
private static final AtomicReference<LoggerContext> DEFAULT_CONTEXT = new AtomicReference<>();
@@ -53,6 +54,13 @@ public class ClassLoaderContextSelector implements ContextSelector {
new ConcurrentHashMap<>();
@Override
+ public void contextShutdown(org.apache.logging.log4j.spi.LoggerContext loggerContext) {
+ if (loggerContext instanceof LoggerContext) {
+ removeContext((LoggerContext) loggerContext);
+ }
+ }
+
+ @Override
public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
return getContext(fqcn, loader, currentContext, null);
}
diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java
new file mode 100644
index 0000000..742fc9b
--- /dev/null
+++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.slf4j;
+
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.Test;
+import org.slf4j.impl.StaticLoggerBinder;
+
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests cleanup of the LoggerContexts.
+ */
+public class LoggerContextTest {
+
+ @Test
+ public void testCleanup() throws Exception {
+ Log4jLoggerFactory factory = (Log4jLoggerFactory) StaticLoggerBinder.getSingleton().getLoggerFactory();
+ factory.getLogger("test");
+ Set<LoggerContext> set = factory.getLoggerContexts();
+ LoggerContext ctx1 = set.toArray(new LoggerContext[0])[0];
+ assertTrue("LoggerContext is not enabled for shutdown", ctx1 instanceof LifeCycle);
+ ((LifeCycle) ctx1).stop();
+ set = factory.getLoggerContexts();
+ assertTrue("Expected no LoggerContexts", set.isEmpty());
+ }
+}
diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java
new file mode 100644
index 0000000..3c6403c
--- /dev/null
+++ b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.slf4j;
+
+import org.apache.logging.log4j.core.LifeCycle;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.Test;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests cleanup of the LoggerContexts.
+ */
+public class LoggerContextTest {
+
+ @Test
+ public void testCleanup() throws Exception {
+ Log4jLoggerFactory factory = (Log4jLoggerFactory) LoggerFactory.getILoggerFactory();
+ factory.getLogger("test");
+ Set<LoggerContext> set = factory.getLoggerContexts();
+ LoggerContext ctx1 = set.toArray(new LoggerContext[0])[0];
+ assertTrue("LoggerContext is not enabled for shutdown", ctx1 instanceof LifeCycle);
+ ((LifeCycle) ctx1).stop();
+ set = factory.getLoggerContexts();
+ assertTrue("Expected no LoggerContexts", set.isEmpty());
+ }
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 18e987b..cff70e8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,6 +30,9 @@
- "remove" - Removed
-->
<release version="2.12.1" date="2019-MM-DD" description="GA Release 2.12.1">
+ <action issue="LOG4J2-2366" dev="rgoers" type="fix">
+ Remove references to LoggerContext when it is shutdown.
+ </action>
<action issue="LOG4J2-2556" dev="rgoers" type="update">
Make Log4j Core optional for Log4j 1.2 API.
</action>