Logging in Spring Boot with SLF4J

Introduction

Server logs are an important part of development, that record the activities or events that the system is performing at any given point in time.

Spring Boot makes use of Apache Commons' Logging for its system logs by default. Additionally, by default you can use any of the logging frameworks under the SLF4J API such as Logback (which is the default), Log4J2, and Java Util Logging in Spring Boot.

Application logging is a powerful development tool that is important for production-level support and debugging. Each log entry contains information such as the timestamp, the actual method being called, a custom log message, and other contextual information. Each log entry also includes an identifier called a logging level.

In this tutorial, we'll take a look at how to perform Logging in Spring Boot, using SLF4J, as well as log levels and the configuration required to customize them, and log groups.

Log Messages in Spring Boot

To enable logging in Spring, import Logger and LoggerFactory from the org.slf4j API library:

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

Afterwards, declare a constant to store the logger service by instantiating a logger for the class using LoggerFactory:

private static final Logger log = LoggerFactory.getLogger(DemoService.class);

This log is usually defined at the very top of the global variable list, since it's a constant. By providing the class you want to log, it's fully set up and ready to be called with the provided messages.

Alternatively, you can just annotate the class with @Slf4j which will automatically generate a logger for the class without having to declare a constant. The annotation supplies a static variable called log which provides the logger utilities by default:

@Slf4j
@Service
public class DemoService {
    //...
}

Note: The most common identifier for the logger is log, though you'll also see logger in a lot of cases. Really, you're free to call it whatever you'd like, but log is succinct and saves you from writing a longer name due to frequent calls.

Logging in Spring Boot comprises of different levels. Every log has a degree of urgency or importance that is associated with a log level. Log levels are configured at runtime and each log request checks the log level to see if the request has equal or higher priority than the log level to be displayed.

If you try logging a message which is considered less important than some threshold, it just won't be logged.

Take this block for example, with messages of log importance in descending order:

public void logLevelDisplay () {
    logger.error("Error message");
    logger.warn("Warning message");
    logger.info("Info message");
    logger.debug("Debug message");
    logger.trace("Trace message");
}

If no configurations are set in application.properties or elsewhere, the log level will default to INFO. So if we were to call the method logLevelDisplay(), it would output something like this:

2020-12-20 12:46:35.880  ERROR 47958 --- [nio-8080-exec-1] c.service.DemoService   : Error message
2020-12-20 12:46:52.976  WARN 47958 --- [nio-8080-exec-1 c.service.DemoService    : Warn message
2020-12-20 12:46:59.104  INFO 47958 --- [nio-8080-exec-1] c.service.DemoService    : Info message

The DEBUG and TRACE messages weren't displayed because the application's log level is set to INFO, which has a higher priority level than the two.

If the log level was set to ERROR, then only the error message would be displayed since it's the highest log level of the 5 built-in levels.

This brings us to an important topic - log levels, which deserve an explanation of their own.

Log Levels in Spring Boot

Logging in Spring Boot comprises of different levels. Every log has a degree of urgency or importance that is associated with a log level. For example, Hibernate SQL queries are categorized as DEBUG, which is a low-urgency log level that is mainly invoked for debugging purposes.

Logging levels allows distinction between the following sample logs:

  • Successfully updated User information: Updated name John to Jon.
  • NullPointerException: id of User is null

The first log is a general informative type of message while the second log is a NullPointerException message.

These logs have different levels of urgency, and distinction between them using log levels is important for filtering out logs that don't have to be output depending on the circumstance. If there's a huge error going on, you don't want it to be cluttered by a bunch of working, low-level INFO logs!

SLF4J provides 5 default logging levels in Spring boot:

ERROR - Error logs are serious issues that affect a significant part of the system or some part of your application has failed to operate. Exceptions are considered ERROR level logs. Other examples of error logs are database connection failures and configuration errors. ERROR logs are the most urgent default log level in SLF4J.

WARN - Warning logs are used to indicate potential problems that might cause errors and should be monitored in case they fail. Of course, the context of a warning is subjective to the developer and the situation so warning logs might vary from system to system.

INFO -INFO is the default logging level that is set by Spring Boot. If no configurations are made, the log level is automatically set to INFO. These types of logs are information that isn't normally needed but is useful in situations like production code debugging or determining when certain data is manipulated.

DEBUG -DEBUG logs include more detailed, specific information that aren't needed in normal situations. This is often set as a log level when a developer is trying to deep trace a problem or a bug that is hard to trace.

TRACE - TRACE is a more granular version of DEBUG. TRACE logs are exhaustive, imagine logging every single operation the system is doing, from starting a service, initializing new variables, and calling methods.

In order of urgency, ERROR is the most urgent while TRACE is the least urgent log. The default log level in Spring Boot is INFO when no manual configuration is set.

Configuring Log Levels in Spring Boot

Log levels can be set in the Spring environment by setting its configurations in application.properties.

The format to set the log level configuration is logging.level.[classpath] = [level]. The classpath is specified since different components of the application can be configured with different log levels, which is especially useful for code isolation and debugging.

To specify a log level for all classes that don't have their own log level settings, the root logger can be set using logging.level.root.

In application.properties:

logging.level.root=INFO
logging.level.com.test=DEBUG
logging.level.com.service.DemoService=WARN

From this configuration, every class except DemoService and the classes under the com.test classpath will have their log levels set to INFO, while the test classes and DemoService have their own specified log levels.

Log Groups

Log groups is a useful way to set logger configurations to a group of classes with different classpaths. An example is if you want to set all the test class log levels to DEBUG in one go. This is possible using the configuration logging.group.[groupName]:

# Initialize log group
logging.group.test=com.test, com.test-prod, com.apptest

# Set log level to log group
logging.level.test=DEBUG

With this approach, you won't have to individually set the log level of all related components all the time.

Conclusion

Knowing about the different log levels is important especially in situations like debugging in production.

Let's say a major bug has been exposed in production, and the current logs do not have enough information to diagnose the root cause of the problem. By changing the log level to DEBUG or TRACE, the logs will show much-needed information to pinpoint crucial details that may lead towards the fix.

In Spring, the log level configurations can be set in the application.properties file which is processed during runtime. Spring supports 5 default log levels, ERROR, WARN, INFO, DEBUG, and TRACE, with INFO being the default log level configuration.