This is an automated email from the ASF dual-hosted git repository.
ckozak 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 81d35d1 LOG4J2-2747: Fix a memory leak using discard and synchronous
queue-full routes
81d35d1 is described below
commit 81d35d1da08ca39f897c62a94d7c730c969016bc
Author: Carter Kozak <[email protected]>
AuthorDate: Tue Dec 24 10:29:26 2019 -0500
LOG4J2-2747: Fix a memory leak using discard and synchronous queue-full
routes
The ring buffer event translator is reset when routes are chosen which
don't clear the object.
---
.../logging/log4j/core/async/AsyncLogger.java | 3 +
.../core/async/RingBufferLogEventTranslator.java | 2 +-
.../log4j/core/GarbageCollectionHelper.java | 2 +-
.../logging/log4j/core/async/BlockingAppender.java | 6 +-
.../core/async/QueueFullAsyncLoggerTest3.java | 131 +++++++++++++++++++++
src/changes/changes.xml | 3 +
6 files changed, 144 insertions(+), 3 deletions(-)
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
index b264b7d..6b0b5d7 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
@@ -237,6 +237,7 @@ public class AsyncLogger extends Logger implements
EventTranslatorVararg<RingBuf
AsyncQueueFullMessageUtil.logWarningToStatusLogger();
logMessageInCurrentThread(translator.fqcn, translator.level,
translator.marker, translator.message,
translator.thrown);
+ translator.clear();
return;
}
final EventRoute eventRoute =
loggerDisruptor.getEventRoute(translator.level);
@@ -247,8 +248,10 @@ public class AsyncLogger extends Logger implements
EventTranslatorVararg<RingBuf
case SYNCHRONOUS:
logMessageInCurrentThread(translator.fqcn, translator.level,
translator.marker, translator.message,
translator.thrown);
+ translator.clear();
break;
case DISCARD:
+ translator.clear();
break;
default:
throw new IllegalStateException("Unknown EventRoute " +
eventRoute);
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
index ec2ff1c..a6f5caf 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEventTranslator.java
@@ -69,7 +69,7 @@ public class RingBufferLogEventTranslator implements
/**
* Release references held by this object to allow objects to be
garbage-collected.
*/
- private void clear() {
+ void clear() {
setBasicValues(null, // asyncLogger
null, // loggerName
null, // marker
diff --git
a/log4j-core/src/test/java/org/apache/logging/log4j/core/GarbageCollectionHelper.java
b/log4j-core/src/test/java/org/apache/logging/log4j/core/GarbageCollectionHelper.java
index ceb652e..544cb09 100644
---
a/log4j-core/src/test/java/org/apache/logging/log4j/core/GarbageCollectionHelper.java
+++
b/log4j-core/src/test/java/org/apache/logging/log4j/core/GarbageCollectionHelper.java
@@ -29,7 +29,7 @@ import static org.junit.Assert.assertTrue;
public final class GarbageCollectionHelper implements Closeable, Runnable {
private static final OutputStream sink = ByteStreams.nullOutputStream();
- public final AtomicBoolean running = new AtomicBoolean();
+ private final AtomicBoolean running = new AtomicBoolean();
private final CountDownLatch latch = new CountDownLatch(1);
private final Thread gcThread = new Thread(new Runnable() {
@Override
diff --git
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java
index c8e5ecd..6e21f34 100644
---
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java
+++
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/BlockingAppender.java
@@ -42,6 +42,7 @@ import org.apache.logging.log4j.core.impl.Log4jLogEvent;
@Plugin(name = "Blocking", category = Core.CATEGORY_NAME, elementType =
Appender.ELEMENT_TYPE, printObject = true)
public class BlockingAppender extends AbstractAppender {
private static final long serialVersionUID = 1L;
+ // logEvents may be nulled to disable event tracking, this is useful in
scenarios testing garbage collection.
public List<LogEvent> logEvents = new CopyOnWriteArrayList<>();
public CountDownLatch countDownLatch = null;
@@ -56,7 +57,10 @@ public class BlockingAppender extends AbstractAppender {
event.getMessage().getFormattedMessage();
// may be a reusable event, make a copy, don't keep a reference to the
original event
- logEvents.add(Log4jLogEvent.createMemento(event));
+ List<LogEvent> events = logEvents;
+ if (events != null) {
+ events.add(Log4jLogEvent.createMemento(event));
+ }
if (countDownLatch == null) {
return;
diff --git
a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/QueueFullAsyncLoggerTest3.java
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/QueueFullAsyncLoggerTest3.java
new file mode 100644
index 0000000..2ec737c
--- /dev/null
+++
b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/QueueFullAsyncLoggerTest3.java
@@ -0,0 +1,131 @@
+/*
+ * 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.async;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.categories.AsyncLoggers;
+import org.apache.logging.log4j.core.GarbageCollectionHelper;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.util.Strings;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests queue full scenarios with pure AsyncLoggers (all loggers async).
+ */
+@RunWith(BlockJUnit4ClassRunner.class)
+@Category(AsyncLoggers.class)
+public class QueueFullAsyncLoggerTest3 extends QueueFullAbstractTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ //FORMAT_MESSAGES_IN_BACKGROUND
+ System.setProperty("log4j.format.msg.async", "true");
+ System.setProperty("log4j2.asyncQueueFullPolicy", "discard");
+
+ System.setProperty("AsyncLogger.RingBufferSize", "128"); // minimum
ringbuffer size
+ System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY,
+ "log4j2-queueFull.xml");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ System.setProperty(Constants.LOG4J_CONTEXT_SELECTOR, Strings.EMPTY);
+ }
+
+ @Rule
+ public LoggerContextRule context = new LoggerContextRule(
+ "log4j2-queueFull.xml", AsyncLoggerContextSelector.class);
+
+ @Before
+ public void before() throws Exception {
+ blockingAppender = context.getRequiredAppender("Blocking",
BlockingAppender.class);
+ }
+
+
+ @Test(timeout = 15000)
+ public void discardedMessagesShouldBeGarbageCollected() throws
InterruptedException {
+ final Logger logger = LogManager.getLogger(this.getClass());
+
+ blockingAppender.logEvents = null;
+ blockingAppender.countDownLatch = new CountDownLatch(1);
+ int count = 200;
+ CountDownLatch garbageCollectionLatch = new CountDownLatch(count);
+ for (int i = 0; i < count; i++) {
+ logger.info(new
CountdownOnGarbageCollectMessage(garbageCollectionLatch));
+ }
+ blockingAppender.countDownLatch.countDown();
+
+ final GarbageCollectionHelper gcHelper = new GarbageCollectionHelper();
+ gcHelper.run();
+ try {
+ assertTrue("Parameter should have been garbage collected",
garbageCollectionLatch.await(30, TimeUnit.SECONDS));
+ } finally {
+ gcHelper.close();
+ }
+ }
+
+ private static final class CountdownOnGarbageCollectMessage implements
Message {
+
+ private final CountDownLatch latch;
+
+ CountdownOnGarbageCollectMessage(CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ @Override
+ public String getFormattedMessage() {
+ return "formatted";
+ }
+
+ @Override
+ public String getFormat() {
+ return null;
+ }
+
+ @Override
+ public Object[] getParameters() {
+ return new Object[0];
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return null;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ latch.countDown();
+ super.finalize();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9fa29a7..9e19be4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -42,6 +42,9 @@
<action issue="LOG4J2-2745" dev="ckozak" type="update">
Update log4j-slf4j18-impl slf4j version to 1.8.0-beta4 from
1.8.0-alpha2.
</action>
+ <action issue="LOG4J2-2747" dev="ckozak" type="fix">
+ Fix a memory leak using fully asynchronous logging when the queue is
full using the 'discard' asynchronous queue full strategy.
+ </action>
</release>
<release version="2.13.0" date="2019-12-11" description="GA Release
2.13.0">
<action issue="LOG4J2-2058" dev="rgoers" type="fix">