3232import org .apache .logging .log4j .Level ;
3333import org .apache .logging .log4j .LogManager ;
3434import org .apache .logging .log4j .Logger ;
35+ import org .apache .logging .log4j .core .Appender ;
36+ import org .apache .logging .log4j .core .LogEvent ;
3537import org .apache .logging .log4j .core .LoggerContext ;
38+ import org .apache .logging .log4j .core .appender .AbstractAppender ;
3639import org .apache .logging .log4j .core .config .Configurator ;
40+ import org .apache .logging .log4j .core .layout .PatternLayout ;
3741import org .apache .logging .log4j .status .StatusConsoleListener ;
3842import org .apache .logging .log4j .status .StatusData ;
3943import org .apache .logging .log4j .status .StatusLogger ;
@@ -181,6 +185,8 @@ public abstract class ESTestCase extends LuceneTestCase {
181185
182186 private static final AtomicInteger portGenerator = new AtomicInteger ();
183187
188+ private static final Collection <String > nettyLoggedLeaks = new ArrayList <>();
189+
184190 @ AfterClass
185191 public static void resetPortCounter () {
186192 portGenerator .set (0 );
@@ -190,8 +196,28 @@ public static void resetPortCounter() {
190196 System .setProperty ("log4j.shutdownHookEnabled" , "false" );
191197 System .setProperty ("log4j2.disable.jmx" , "true" );
192198
199+ // Enable Netty leak detection and monitor logger for logged leak errors
200+ System .setProperty ("io.netty.leakDetection.level" , "advanced" );
201+ String leakLoggerName = "io.netty.util.ResourceLeakDetector" ;
202+ Logger leakLogger = LogManager .getLogger (leakLoggerName );
203+ Appender leakAppender = new AbstractAppender (leakLoggerName , null ,
204+ PatternLayout .newBuilder ().withPattern ("%m" ).build ()) {
205+ @ Override
206+ public void append (LogEvent event ) {
207+ String message = event .getMessage ().getFormattedMessage ();
208+ if (Level .ERROR .equals (event .getLevel ()) && message .contains ("LEAK:" )) {
209+ synchronized (nettyLoggedLeaks ) {
210+ nettyLoggedLeaks .add (message );
211+ }
212+ }
213+ }
214+ };
215+ leakAppender .start ();
216+ Loggers .addAppender (leakLogger , leakAppender );
217+
193218 // shutdown hook so that when the test JVM exits, logging is shutdown too
194219 Runtime .getRuntime ().addShutdownHook (new Thread (() -> {
220+ leakAppender .stop ();
195221 LoggerContext context = (LoggerContext ) LogManager .getContext (false );
196222 Configurator .shutdown (context );
197223 }));
@@ -438,6 +464,13 @@ protected static void checkStaticState(boolean afterClass) throws Exception {
438464 statusData .clear ();
439465 }
440466 }
467+ synchronized (nettyLoggedLeaks ) {
468+ try {
469+ assertThat (nettyLoggedLeaks , empty ());
470+ } finally {
471+ nettyLoggedLeaks .clear ();
472+ }
473+ }
441474 }
442475
443476 // this must be a separate method from other ensure checks above so suite scoped integ tests can call...TODO: fix that
0 commit comments