Comprehensive test suite and testing utilities for Apache Log4j core logging implementation
—
Test appenders for capturing and analyzing log events during testing. These specialized appenders provide essential functionality for verifying logging behavior, testing error scenarios, and validating threading behavior in Log4j applications.
The primary test appender for capturing log events in memory. Provides thread-safe collection of events, messages, and raw data with immutable snapshot access.
/**
* Primary test appender that collects log events in lists for verification
* Plugin annotation: @Plugin(name = "List", category = Core.CATEGORY_NAME)
*/
@Plugin(name = "List", category = Core.CATEGORY_NAME)
public class ListAppender extends AbstractAppender {
/**
* Returns immutable snapshot of captured log events
* @return List of LogEvent objects
*/
public List<LogEvent> getEvents();
/**
* Returns immutable snapshot of formatted log messages
* @return List of formatted message strings
*/
public List<String> getMessages();
/**
* Returns immutable snapshot of raw serialized data
* @return List of byte arrays containing serialized data
*/
public List<byte[]> getData();
/**
* Clears all captured data and resets the appender
* @return This ListAppender instance for method chaining
*/
public ListAppender clear();
/**
* Retrieves a named ListAppender instance from the logger context
* @param name The name of the appender to retrieve
* @return The named ListAppender instance or null if not found
*/
public static ListAppender getListAppender(String name);
/**
* CountDownLatch for synchronizing asynchronous testing scenarios
* Volatile field for thread-safe access during concurrent testing
*/
public volatile CountDownLatch countDownLatch;
}Usage Examples:
import org.apache.logging.log4j.core.test.appender.ListAppender;
import org.apache.logging.log4j.core.test.junit.LoggerContextRule;
import org.junit.Rule;
import org.junit.Test;
public class ListAppenderTest {
@Rule
public LoggerContextRule context = new LoggerContextRule("log4j2-test.xml");
@Test
public void testEventCapture() {
ListAppender appender = context.getListAppender("TestList");
Logger logger = context.getLogger();
// Log some events
logger.info("Info message");
logger.warn("Warning message");
logger.error("Error message");
// Verify captured events
List<LogEvent> events = appender.getEvents();
assertEquals(3, events.size());
assertEquals(Level.INFO, events.get(0).getLevel());
assertEquals("Info message", events.get(0).getMessage().getFormattedMessage());
// Verify captured messages
List<String> messages = appender.getMessages();
assertEquals(3, messages.size());
assertTrue(messages.contains("Info message"));
// Clear for next test
appender.clear();
assertEquals(0, appender.getEvents().size());
}
@Test
public void testAsynchronousLogging() throws InterruptedException {
ListAppender appender = context.getListAppender("AsyncTestList");
appender.countDownLatch = new CountDownLatch(1);
Logger logger = context.getLogger();
logger.info("Async message");
// Wait for async processing
assertTrue(appender.countDownLatch.await(5, TimeUnit.SECONDS));
assertEquals(1, appender.getEvents().size());
}
}Appender that blocks on append operations for testing threading scenarios and deadlock detection.
/**
* Appender that blocks for testing threading scenarios
* Plugin annotation: @Plugin(name = "Block", category = "Core")
*/
@Plugin(name = "Block", category = "Core")
public class BlockingAppender extends AbstractAppender {
/**
* Creates a blocking appender with the specified name
* @param name The name for the appender
* @return New BlockingAppender instance
*/
public static BlockingAppender createAppender(String name);
/**
* Indicates whether the appender is currently running/blocking
* Volatile field for thread-safe access
*/
public volatile boolean running;
}Appender that always fails for testing error handling behavior.
/**
* Appender that always fails for error handling testing
* Plugin annotation: @Plugin(name = "AlwaysFail", category = "Core")
*/
@Plugin(name = "AlwaysFail", category = "Core")
public class AlwaysFailAppender extends AbstractAppender {
/**
* Creates an appender that always fails on append operations
* @param name The name for the appender
* @return New AlwaysFailAppender instance
*/
public static AlwaysFailAppender createAppender(String name);
}Appender that fails once then succeeds for testing resilience and recovery behavior.
/**
* Appender that fails once then succeeds for resilience testing
* Plugin annotation: @Plugin(name = "FailOnce", category = "Core")
*/
@Plugin(name = "FailOnce", category = "Core")
public class FailOnceAppender extends AbstractAppender {
/**
* Creates an appender that fails on first append then succeeds
* @param name The name for the appender
* @return New FailOnceAppender instance
*/
public static FailOnceAppender createAppender(String name);
/**
* Enum defining types of exceptions to throw on failure
*/
public enum ThrowableClassName {
RUNTIME_EXCEPTION,
LOGGING_EXCEPTION,
IO_EXCEPTION
}
}Memory-based appender for testing output stream functionality.
/**
* Memory-based appender extending AbstractOutputStreamAppender
*/
public class InMemoryAppender extends AbstractOutputStreamAppender {
/**
* Creates an in-memory appender for testing stream operations
* @param name The name for the appender
* @param layout The layout to use for formatting
* @return New InMemoryAppender instance
*/
public static InMemoryAppender createAppender(String name, Layout<? extends Serializable> layout);
/**
* Gets the captured output as a string
* @return String representation of captured output
*/
public String getOutput();
/**
* OutputStreamManager implementation for memory-based operations
*/
public static class InMemoryManager extends OutputStreamManager {
public byte[] getBytes();
public String getString();
}
}ListAppender with encoding support for testing character encoding scenarios.
/**
* ListAppender with encoding support
* Extends ListAppender with character encoding capabilities
*/
public class EncodingListAppender extends ListAppender {
/**
* Creates an encoding-aware list appender
* @param name The name for the appender
* @param encoding The character encoding to use
* @return New EncodingListAppender instance
*/
public static EncodingListAppender createAppender(String name, String encoding);
/**
* Gets the character encoding used by this appender
* @return The character encoding name
*/
public String getEncoding();
}// Configure ListAppender in log4j2-test.xml
ListAppender appender = context.getListAppender("TestAppender");
Logger logger = context.getLogger();
logger.info("Test message");
List<LogEvent> events = appender.getEvents();
assertEquals(1, events.size());
assertEquals(Level.INFO, events.get(0).getLevel());// Use AlwaysFailAppender to test error handling
AlwaysFailAppender failAppender = AlwaysFailAppender.createAppender("FailTest");
// Configure and test error scenarios// Use BlockingAppender for threading tests
BlockingAppender blockingAppender = BlockingAppender.createAppender("BlockTest");
// Test concurrent logging scenarios// Use FailOnceAppender to test recovery
FailOnceAppender failOnceAppender = FailOnceAppender.createAppender("ResilienceTest");
// Test that logging recovers after initial failureInstall with Tessl CLI
npx tessl i tessl/maven-org-apache-logging-log4j--log4j-core-test