Logging in Spring Boot

Logging is a critical aspect of any application, allowing developers to track the application's behavior, identify issues, and debug problems. Spring Boot provides robust support for various logging frameworks, making it easy to configure and use logging in your application. In this tutorial, we will explore how to set up and use logging in a Spring Boot application.

Prerequisites

  • JDK 17 or later
  • Maven or Gradle
  • IDE (IntelliJ IDEA, Eclipse, etc.)

Step 1: Set Up a Spring Boot Project

1.1 Create a New Spring Boot Project

Use Spring Initializr to create a new project with the following dependencies:

  • Spring Web

Download and unzip the project, then open it in your IDE.

1.2 Configure application.properties

Set up the application properties for your project. This file is located in the src/main/resources directory.

# src/main/resources/application.properties

server.port=8080

Step 2: Understanding Spring Boot's Default Logging Configuration

Spring Boot uses Logback as the default logging framework. When you create a new Spring Boot project, it comes with default logging configuration, which includes:

  • Logging output to the console.
  • Logging levels (ERROR, WARN, INFO, DEBUG, TRACE) set to INFO by default.

Step 3: Customizing Logging Configuration

You can customize the logging configuration in Spring Boot by modifying the application.properties file or by providing a custom Logback configuration file.

3.1 Customize Logging Levels in application.properties

You can set logging levels for specific packages or classes in the application.properties file.

# src/main/resources/application.properties

# Set the root logging level to WARN
logging.level.root=WARN

# Set the logging level for a specific package to DEBUG
logging.level.com.example.demo=DEBUG

3.2 Create a Custom Logback Configuration File

For more advanced logging configurations, you can create a custom Logback configuration file named logback-spring.xml in the src/main/resources directory.

<!-- src/main/resources/logback-spring.xml -->

<configuration>

    <!-- Define an appender for console output -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Define an appender for file output -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/spring-boot-application.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/spring-boot-application.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Set the root logger level and add appenders -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>

    <!-- Set the logging level for a specific package -->
    <logger name="com.example.demo" level="DEBUG" />

</configuration>

Explanation:

  • <appender>: Defines where the log output will go. The example includes console and file appenders.
  • <encoder>: Defines the format of the log messages.
  • <rollingPolicy>: Defines how log files should be rolled (archived) and how many archives to keep.
  • <root>: Sets the root logger level and associates appenders with it.
  • <logger>: Sets the logging level for a specific package.

Step 4: Using Logging in Your Application

4.1 Add Logging to Your Classes

You can use logging in your Spring Boot application by creating a logger instance in your classes. Spring Boot supports SLF4J (Simple Logging Facade for Java) as the preferred logging API.

package com.example.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);

    @GetMapping("/hello")
    public String hello() {
        logger.info("INFO log message");
        logger.debug("DEBUG log message");
        logger.warn("WARN log message");
        logger.error("ERROR log message");
        return "Hello, World!";
    }
}

Explanation:

  • LoggerFactory.getLogger(HelloController.class): Creates a logger instance for the HelloController class.
  • logger.info(), logger.debug(), logger.warn(), logger.error(): Log messages at different levels.

Step 5: Testing the Logging Configuration

5.1 Run the Application

Run the Spring Boot application using your IDE or the command line:

./mvnw spring-boot:run

5.2 Access the Endpoint

Open your browser and navigate to http://localhost:8080/hello. You should see the log messages in the console and in the log file (if configured).

Step 6: Advanced Logging Configuration (Optional)

6.1 Add a Log File Appender for Different Log Levels

You can add different log file appenders for different log levels in the logback-spring.xml file.

<!-- src/main/resources/logback-spring.xml -->

<configuration>

    <!-- Define an appender for console output -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Define an appender for info level logs -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/info.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Define an appender for error level logs -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Set the root logger level and add appenders -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>

    <!-- Set the logging level for a specific package -->
    <logger name="com.example.demo" level="DEBUG" />

</configuration>

Explanation:

  • LevelFilter: Filters log messages based on the log level.

6.2 Use MDC (Mapped Diagnostic Context)

MDC is used to add contextual information to your logs, such as user ID or request ID.

import org.slf4j.MDC;
import java.util.UUID;

public class LoggingExample {

    public void logWithContext() {
        MDC.put("requestId", UUID.randomUUID().toString());
        logger.info("Log message with context");
        MDC.clear(); // Clear the MDC context
    }
}

Explanation:

  • MDC.put("key", "value"): Adds a key-value pair to the MDC context.
  • MDC.clear(): Clears the MDC context.

6.3 Customize Log Format

You can customize the log format in the logback-spring.xml file by modifying the <pattern> element within the <encoder> element.

<!-- src/main/resources/logback-spring.xml -->

<configuration>

    <!-- Define an appender for console output -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Define an appender for info level logs -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/info.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Define an appender for error level logs -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Set the root logger level and add appenders -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>

    <!-- Set the logging level for a specific package -->
    <logger name="com.example.demo" level="DEBUG" />

</configuration>

Explanation:

  • %d{yyyy-MM-dd HH:mm:ss}: Adds the date and time of the log message.
  • %thread: Adds the thread name.
  • %-5level: Adds the log level with a fixed width of 5 characters.
  • %logger{36}: Adds the logger name, truncated to 36 characters if necessary.
  • %msg: Adds the log message.
  • %n: Adds a new line.

Step 7: Advanced Logging Features

7.1 Add Logback Extensions

Logback supports various extensions that can enhance your logging capabilities. For example, you can add the Logback-access module to capture HTTP request and response logs.

Add the dependency to your pom.xml (for Maven):

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>1.2.3</version>
</dependency>

Add the dependency to your build.gradle (for Gradle):

implementation 'ch.qos.logback:logback-access:1.2.3'

Configure logback-access.xml in the src/main/resources directory:

<!-- src/main/resources/logback-access.xml -->

<configuration>
    <appender name="ACCESS-CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date %status %method %requestURL [%thread] %responseTime ms%n</pattern>
        </encoder>
    </appender>

    <appender name="ACCESS-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/access.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/access.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date %status %method %requestURL [%thread] %responseTime ms%n</pattern>
        </encoder>
    </appender>

    <access>
        <appender-ref ref="ACCESS-CONSOLE" />
        <appender-ref ref="ACCESS-FILE" />
    </access>
</configuration>

7.2 Integrate with External Log Management Systems

Integrating with external log management systems like ELK Stack (Elasticsearch, Logstash, and Kibana) or Splunk can help you analyze and monitor your logs more effectively.

For example, to integrate with Logstash, you can use the Logstash encoder for Logback:

Add the dependency to your pom.xml (for Maven):

<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>6.6</version>
</dependency>

Add the dependency to your build.gradle (for Gradle):

implementation 'net.logstash.logback:logstash-logback-encoder:6.6'

Configure the Logstash encoder in your logback-spring.xml:

<!-- src/main/resources/logback-spring.xml -->

<configuration>

    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>localhost:5044</destination>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
    </appender>

    <root level="INFO">
        <appender-ref ref="LOGSTASH" />
    </root>

</configuration>

Explanation:

  • LogstashTcpSocketAppender: Sends log messages to a Logstash TCP socket.
  • <destination>: Specifies the Logstash server and port.

Step 8: Testing the Advanced Configuration

8.1 Run the Application

Run the Spring Boot application using your IDE or the command line:

./mvnw spring-boot:run

8.2 Test the Logging

Access your application endpoints and check the log output in the console and log files. Ensure that the log messages are formatted correctly and are sent to the configured log destinations.

Conclusion

In this tutorial, you have learned how to set up and customize logging in a Spring Boot application. We covered:

  • Understanding Spring Boot's default logging configuration
  • Customizing logging levels in application.properties
  • Creating a custom Logback configuration file
  • Adding logging to your application classes
  • Using advanced logging features like MDC and Logback extensions
  • Integrating with external log management systems

By leveraging Spring Boot's powerful logging capabilities, you can effectively monitor and debug your application, ensuring it runs smoothly and efficiently.


Comments