Unit testing a custom logger in log4j2

In this period I’m upgrading a Spring Boot project from 1.3 –> 1.5 –> 2.0. One of the steps is to get rid of log4j 1.x.

In the project there is custom logger, but unfortunately its unit test with log4j2 is no more working and we receive the following error:

mockito wanted but not invoked, Actually there were zero interactions with this mock

Basically this exact same error in Stackoverflow.

Let’s see how we can make a new test using log4j2.

Take a look to the log4j2 library tests

A very good advice is to take a look to the tests used in the log4j2 library, to see how people are doing. This is the JUnit test I took as an example. In my case I don’t want to test a custom logging level, I just want to test the logger itself. Though, from this test you can basically see what you need in terms of dependencies and classes.

Create a custom logger

You can create one manually yourself, otherwise Log4j2 provides a useful tool to generate one automatically. You can take a look to the official documentation here. You just need to run the following command (here it’s creating a class MyLogger.java with the standard info(), debug(), error(), but you could use your own names such as defcon1 etc.. if you need a more domain specific naming):

$ java -cp log4j-core-2.11.0.jar org.apache.logging.log4j.core.tools.CustomLoggerGenerator com.demo.MyLogger info=1 debug=2 error=3 > MyLogger.java

Frankly, I struggled a bit to make this work. Be sure to run it from the same location where log4j-core-2.11.0.jar is stored.

Gradle dependencies

The following are the dependencies you need:

	compile('org.apache.logging.log4j:log4j-api:2.11.0')
	compile('org.apache.logging.log4j:log4j-core:2.11.0')
	compile('org.apache.logging.log4j:log4j-core:2.11.0:tests')

The key here is the third one: log4j-core:2.11.0:tests. This includes a jar that contains two classes useful for the tests: org.apache.logging.log4j.junit.LoggerContextRule and org.apache.logging.log4j.test.appender.ListAppender.

ListAppender

You have your gradle dependencies and your custom logger. Now you need to set up an Appender. In Log4j 1.x you could use

@Mock
private Appender mockAppender;
@Captor
private ArgumentCaptor<LoggingEvent> captorLoggingEvent;

but LoggingEvent is no more included in log4j2. In order to get the logger events now you can use ListAppender, which provides the events in all the appenders in the configuration. So, let’s create this configuration with the appender list. You have multiple choices here:

I went for the latter and I created my test/resources/logj2.xml that will be used only by my JUnit test. This one:

<Configuration status="DEBUG" name="TestCustomLevels">
<Appenders>
    <Console name="STDOUT">
        <PatternLayout pattern="%m%n"/>
    </Console>
    <List name="List1"/>
</Appenders>
<Loggers>
    <Root level="DEBUG">
        <AppenderRef ref="STDOUT"/>
        <AppenderRef ref="List1"/>
    </Root>
</Loggers>
</Configuration>

JUnit

Now you can test your custom logger like so (in this example I have created a Spring Boot project in IntelliJ to have the closest conditions possible to the project I am migrating):

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

	@ClassRule
	public static LoggerContextRule context = new LoggerContextRule("log4j2.xml");
	private ListAppender listAppender;

	@Before
	public void before() {
		listAppender = context.getListAppender("List1").clear();
	}

	@Test
	public void LoggerTest() {
		MyLogger logger = MyLogger.create(DemoApplicationTests.class);
		final List<LogEvent> events = listAppender.getEvents();
		assertThat(events, hasSize(0));

		logger.info("TEST");
		assertThat(events, hasSize(1));

		assertEquals("TEST", events.get(0).getMessage().toString());
	}
}

Download this example

The entire source code is available on my github.

Comments

comments powered by Disqus