TESTS: Check for Netty resource leaks (#31861)

* Enabled advanced leak detection when loading `EsTestCase`
* Added custom `Appender` to collect leak logs and check for logged errors in a way similar to what is done for the `StatusLogger`
* Fixes #20398
This commit is contained in:
Armin Braun 2018-07-20 09:12:32 +02:00 committed by GitHub
parent a9a9598e02
commit 24068a773d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 33 additions and 0 deletions

View File

@ -32,8 +32,12 @@ import com.carrotsearch.randomizedtesting.rules.TestRuleAdapter;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.status.StatusConsoleListener; import org.apache.logging.log4j.status.StatusConsoleListener;
import org.apache.logging.log4j.status.StatusData; import org.apache.logging.log4j.status.StatusData;
import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.status.StatusLogger;
@ -183,6 +187,8 @@ public abstract class ESTestCase extends LuceneTestCase {
private static final AtomicInteger portGenerator = new AtomicInteger(); private static final AtomicInteger portGenerator = new AtomicInteger();
private static final Collection<String> nettyLoggedLeaks = new ArrayList<>();
@AfterClass @AfterClass
public static void resetPortCounter() { public static void resetPortCounter() {
portGenerator.set(0); portGenerator.set(0);
@ -192,8 +198,28 @@ public abstract class ESTestCase extends LuceneTestCase {
System.setProperty("log4j.shutdownHookEnabled", "false"); System.setProperty("log4j.shutdownHookEnabled", "false");
System.setProperty("log4j2.disable.jmx", "true"); System.setProperty("log4j2.disable.jmx", "true");
// Enable Netty leak detection and monitor logger for logged leak errors
System.setProperty("io.netty.leakDetection.level", "advanced");
String leakLoggerName = "io.netty.util.ResourceLeakDetector";
Logger leakLogger = LogManager.getLogger(leakLoggerName);
Appender leakAppender = new AbstractAppender(leakLoggerName, null,
PatternLayout.newBuilder().withPattern("%m").build()) {
@Override
public void append(LogEvent event) {
String message = event.getMessage().getFormattedMessage();
if (Level.ERROR.equals(event.getLevel()) && message.contains("LEAK:")) {
synchronized (nettyLoggedLeaks) {
nettyLoggedLeaks.add(message);
}
}
}
};
leakAppender.start();
Loggers.addAppender(leakLogger, leakAppender);
// shutdown hook so that when the test JVM exits, logging is shutdown too // shutdown hook so that when the test JVM exits, logging is shutdown too
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
leakAppender.stop();
LoggerContext context = (LoggerContext) LogManager.getContext(false); LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configurator.shutdown(context); Configurator.shutdown(context);
})); }));
@ -440,6 +466,13 @@ public abstract class ESTestCase extends LuceneTestCase {
statusData.clear(); statusData.clear();
} }
} }
synchronized (nettyLoggedLeaks) {
try {
assertThat(nettyLoggedLeaks, empty());
} finally {
nettyLoggedLeaks.clear();
}
}
} }
// this must be a separate method from other ensure checks above so suite scoped integ tests can call...TODO: fix that // this must be a separate method from other ensure checks above so suite scoped integ tests can call...TODO: fix that