Testing Your SLF4J Logs

Logging is a very important part of your application. You can be the best developer and never write a bug, but a service you depend on may go down, or the framework you're using may have an issue, so you need to be able to diagnose it without attaching a debugger to your production server. But if you're not testing that your logging works, you won't be sure until one of these disaster scenarios occur whether it's actually hooked in correctly, as well as not being able to catch if someone accidentally removes a logging line in a refactor.

A very popular logging library in the Java ecosystem is SLF4J, and we'll look at how we can test we've set things up correctly.

The repository for this article can be found at jamietanna/slf4j-testing, and we use slf4j-test, as shared by Jack Gough.

Let's say that we have this example class that does some processing of data, as well as logging:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ClassThatLogs {

  private static final Logger LOGGER = LoggerFactory.getLogger(ClassThatLogs.class);

  public void doSomething(boolean logErrors) {
    LOGGER.debug("The boolean passed in has value {}", logErrors);
    if (logErrors) {
      LOGGER.error("this is because there's a boolean=true");
    }
    LOGGER.info("this is happening no matter what");
  }
}

We can follow the Getting Started guide and add the slf4j-test dependency to our codebase, then write a test class (in this example using AssertJ) to make it easier to assert that the logs are present at the right level:

import static org.assertj.core.api.Assertions.assertThat;
import static uk.org.lidalia.slf4jtest.LoggingEvent.debug;
import static uk.org.lidalia.slf4jtest.LoggingEvent.error;
import static uk.org.lidalia.slf4jtest.LoggingEvent.info;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import uk.org.lidalia.slf4jtest.TestLogger;
import uk.org.lidalia.slf4jtest.TestLoggerFactory;

class ClassThatLogsTest {

  private final TestLogger logger = TestLoggerFactory.getTestLogger(ClassThatLogs.class);
  private final ClassThatLogs sut = new ClassThatLogs();

  @AfterEach
  void tearDown() {
    logger.clear();
  }

  @Test
  void methodLogsErrorWhenBooleanIsTrue() {
    // given

    // when
    sut.doSomething(true);

    // then
    assertThat(logger.getLoggingEvents()).contains(error("this is because there's a boolean=true"));
  }

  @Test
  void methodDoesNotLogErrorWhenBooleanIsFalse() {
    // given

    // when
    sut.doSomething(false);

    // then
    assertThat(logger.getLoggingEvents())
        .doesNotContain(error("this is because there's a boolean=true"));
  }

  @Test
  void methodLogsInfoRegardless() {
    // given

    // when
    sut.doSomething(false);

    // then
    assertThat(logger.getLoggingEvents()).contains(info("this is happening no matter what"));
  }

  @Test
  void methodLogsFormatStringsInDebugMode() {
    // given

    // when
    sut.doSomething(false);

    // then
    assertThat(logger.getLoggingEvents())
        .contains(debug("The boolean passed in has value {}", false));
  }
}

We can see that this is fairly straightforward, and allows us to assert clearly on what is being logged.

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #testing #java #slf4j.

This post was filed under articles.

Related Posts

Other posts you may be interested in:

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.