Explain Codes LogoExplain Codes Logo

How to intercept SLF4J (with logback) logging via a JUnit test?

java
logging
junit
test-environment
Alex KataevbyAlex Kataev·Dec 24, 2024
TLDR

Quick and easy way to intercept SLF4J logs in JUnit tests, is to plug in a Logback Test Appender. Here's an illustrative code snippet:

import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; import ch.qos.logback.classic.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; // Custom appender, kind of a stenographer for logs class CapturingAppender extends AppenderBase<ILoggingEvent> { List<ILoggingEvent> capturedEvents = new CopyOnWriteArrayList<>(); @Override protected void append(ILoggingEvent eventObject) { capturedEvents.add(eventObject); // Gotcha. You ain't going nowhere, log! } // Getter omitted for brevity } // In your test CapturingAppender captor = new CapturingAppender(); // Your secret agent Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); // The unsuspecting target rootLogger.addAppender(captor); // Deployed agent! captor.start(); // Agent, you may start the capture. // After executing actions known to trigger logging assertThat(captor.capturedEvents, hasItem(hasProperty("message", is("Expected message"))));

How to analyse the captor? Simple. Invoke captor.capturedEvents.

Diving deeper into SLF4J logging interception

Setting the stage for testing

Before getting your test spy to work, make sure to setup a consistent test environment. Use logback-test.xml to define configurations specific to your test environment, like limiting logging levels.

More readable assertions

With assertion libraries like AssertJ or Hamcrest, you can create assertions that are easily understandable, maintaining simplicity in complex tests.

Concurrent tests - a special case

With potential concurrent modifications in multi-threaded tests, a thread-safe implementation like CopyOnWriteArrayList ensures data integrity.

Cleaning up post-test

In the @AfterEach method, stops and detaches appenders for a clean slate for the next test, preventing unexpected side effects.

Let's talk about logging events

The ILoggingEvent interface contains various context information like the message, logger name, logging level, which can be very helpful for precise data analysis.

Exploring other options for log testing

Consider slf4j-test for scenarios where only testing log with native Logback may not be the best choice.

Mockito to the Rescue

When you want to just check the logging behaviour, mock SLF4J appenders with Mockito. All this without worrying about the actual log messages.

Opting for custom logging interception strategies

You might need to configure your test appender for complex cases like filtering log events.

Getting the best performance

Your logging capture setup should be efficient for tests with heavy logging.

Boss-level assertions

For complex pattern matching, or where log order matters, use advanced assertion techniques.