When you create an application, it is important to keep a record of the inputs, processes, outputs, and results of the application. These records can help developers with the task of debugging and maintaining code. The tool you can use to create them is logging. Logging can be used in a variety of situations, ranging from errors to simple informational outputs to confirm that a process has been completed. In this topic, we will see how we can set up and implement logging in Spring.

Adding logging to a Spring project

For our logging, we are going to use a component called Logback. This component will allow you to produce configurable log files, that can track activities within your application. The logback component is automatically added via spring-boot-starter, since it is the default logging component used by Spring. The logback component is accessible via an external interface called slf4j.

With this, we can now create a logging class for our application. To do this, we will create a new component that handles the logging process. To use these components in our application, we will import them from slf4j. The slf4j components that are imported, namely Logger and LoggerFactory, allow us to construct objects to interface with Logback. The first component we have is an object called Logger. This object implements various functionalities related to writing to logs from the application. The Logger object is constructed using the LoggerFactory.getLogger method, which provides context for the Loggerobject to run in. With this object, you can use various methods to log data. The code below shows an example of how the Logger object can be constructed and used to log data to the console.

import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
public class LoggingController {
    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingController.class);

    public void log() {
        LOGGER.info("This is an INFO level log message!");
    }
}

To understand the logging process better, let's discuss the info method in more detail. In Logback, there is a set number of levels for logging. Here are these levels:

  1. Error: Used to track information when an application fails to execute successfully.
  2. Warn: Used when unexpected events happen that are not critical to the functionality of the application.
  3. Info: Used to log general information about the execution of the program.
  4. Debug: Used to help track program execution for fixing bugs or issues.
  5. Trace: Used to create more detailed and high-volume sets of information about program execution.

When we use the info method, the text provided is logged at the info level. The output related to this log message is similar to one shown below.

12:05:30.102 [main] INFO com.example.demo.LoggingController - This is an INFO level log message!

You can see that the log message has INFO in front of it, indicating the log level used. Another interesting feature of the log message is the fact that it shows the component that printed the log message, in this case, LoggingController. This is possible because when we created our Logger object, we gave it a reference to the class we are logging for, LoggingController.class .

Logging multiple components

One important consideration to make when setting up logging is how to handle multiple application components. Suppose we want to add a second component named GreetingController to our previous code. We can set up a Logger object for this class in a similar way to the LoggingController class, however, to get the log to show the class we need, we provide it with GreetingController.class as an argument. Here is the full code for the component:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class GreetingController {
    private static final Logger LOGGER = LoggerFactory.getLogger(GreetingController.class);

    public void greetUser(String greeting) {

        if (greeting.isEmpty()) {
            LOGGER.error("Provided empty greeting!");
            throw new IllegalArgumentException();
        }

        System.out.println(greeting);
        LOGGER.info("Greeting successfully sent!");
    }
}

This class incorporates different types of errors to demonstrate what they look like in the log. To trigger the error case for this class, we just need to run the greetUser method with an empty string as an argument. For example, the code below shows one way we could trigger the error through a CommandLineRunner component.

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class Runner implements CommandLineRunner {
    private final LoggingController log;
    private final GreetingController greet;

    public Runner(LoggingController log, GreetingController greet) {
        this.log = log;
        this.greet = greet;
    }

    @Override
    public void run(String... args) throws InterruptedException {
        this.log.log();
        this.greet.greetUser("");
    }
}

When this is run, the following will be displayed in the log.