JSON Logging with Spring Boot

JSON Logging with Spring Boot

0 Comments

JSON Logging is easy to setup with Spring Boot. Frequently, Spring Boot applications are deployed in distributed environments such as Kubernetes. In these deployments, you will likely be setting up consolidated logging.

With consolidated logging, the log output of many applications is consolidated into a single source. To be useful, the consolidated logs need to be searchable. To aid in searching, often the logs are formatted to JSON.

There are several options to use, one of them is Logstash. Logstash is widely accepted and is easy to configure with Spring Boot. Below, I’ll show you how easy this is to setup with Spring Boot.

The examples used in this post are available here on GitHub.

JSON Logging Project Dependencies

First we need to add the required dependencies to our project.

Maven

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

Gradle

implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version: '8.0'

Logback Configuration for JSON Logging

Spring Boot uses Logback for its logging configuration. To configure Logback to support JSON logging, add the following file to your resources directory. By default, Spring Boot will look for a configuration file called logback-spring.xml for the logback configuration.

logback-spring.xml

<configuration>

   <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
   <include resource="org/springframework/boot/logging/logback/console-appender.xml" />

   <springProperty scope="context" name="appName" source="spring.application.name"/>

   <springProfile name="!json-logs">
      <include resource="org/springframework/boot/logging/logback/base.xml" />
   </springProfile>

   <springProfile name="json-logs">
         <appender name="jsonConsoleAppender" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
          </appender>
          
          <format>
             <label>
               <pattern>app=${appName},host=${HOSTNAME},traceID=%X{traceId:-NONE},level=%level</pattern>
           </label>
            <message>
                <pattern>${FILE_LOG_PATTERN}</pattern>
            </message>
            <sortByTime>true</sortByTime>
        </format>
        
        <root level="INFO">
            <appender-ref ref="jsonConsoleAppender" />
        </root>
   </springProfile>
</configuration>

This configuration does the following:

  • Imports the Spring Boot default logback configuration
  • Sets the application name
  • Uses normal console output if the profile json-logs is not active
  • Uses Logstash JSON output is the profile `json-logs is active

Having the profile property is handy for development. JSON logs are hard to read in the console, thus you can use normal log output in development. And then use JSON output when the application is deployed.

You can learn more about Logback configuration in my previous blog post about Logback.

Try it out!

To test out the log output, add the following class to your Spring Boot project. This will just generate some log messages we can see in the output.

@Slf4j
@Component
public class BootStrap implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        log.debug("Debugging log");
        log.info("Info log");
        log.warn("Warning log");
        log.error("Error log");
    }
}

In a minimal Spring Boot application, this will produce the following output.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.4)

2024-10-13T12:56:05.655-04:00  INFO 95924 --- [json-logging] [           main] g.s.jsonlogging.JsonLoggingApplication   : Starting JsonLoggingApplication using Java 21.0.4 with PID 95924 (/Users/jt/src/springframework.guru/sfg-blog-posts/json-logging/target/classes started by jt in /Users/jt/src/springframework.guru/sfg-blog-posts)
2024-10-13T12:56:05.657-04:00  INFO 95924 --- [json-logging] [           main] g.s.jsonlogging.JsonLoggingApplication   : No active profile set, falling back to 1 default profile: "default"
2024-10-13T12:56:06.108-04:00  INFO 95924 --- [json-logging] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-10-13T12:56:06.115-04:00  INFO 95924 --- [json-logging] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-10-13T12:56:06.116-04:00  INFO 95924 --- [json-logging] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.30]
2024-10-13T12:56:06.137-04:00  INFO 95924 --- [json-logging] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-10-13T12:56:06.137-04:00  INFO 95924 --- [json-logging] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 458 ms
2024-10-13T12:56:06.322-04:00  INFO 95924 --- [json-logging] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-10-13T12:56:06.327-04:00  INFO 95924 --- [json-logging] [           main] g.s.jsonlogging.JsonLoggingApplication   : Started JsonLoggingApplication in 0.946 seconds (process running for 1.268)
2024-10-13T12:56:06.328-04:00  INFO 95924 --- [json-logging] [           main] g.s.jsonlogging.boot.BootStrap           : Info log
2024-10-13T12:56:06.328-04:00  WARN 95924 --- [json-logging] [           main] g.s.jsonlogging.boot.BootStrap           : Warning log
2024-10-13T12:56:06.328-04:00 ERROR 95924 --- [json-logging] [           main] g.s.jsonlogging.boot.BootStrap           : Error log

When you set the profile json-logs active, you will receive the following output.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.4)

{"@timestamp":"2024-10-13T13:01:44.931694-04:00","@version":"1","message":"Starting JsonLoggingApplication using Java 21.0.4 with PID 95967 (/Users/jt/src/springframework.guru/sfg-blog-posts/json-logging/target/classes started by jt in /Users/jt/src/springframework.guru/sfg-blog-posts)","logger_name":"guru.springframework.jsonlogging.JsonLoggingApplication","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:44.937666-04:00","@version":"1","message":"The following 1 profile is active: \"json-logs\"","logger_name":"guru.springframework.jsonlogging.JsonLoggingApplication","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.412153-04:00","@version":"1","message":"Tomcat initialized with port 8080 (http)","logger_name":"org.springframework.boot.web.embedded.tomcat.TomcatWebServer","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.419088-04:00","@version":"1","message":"Starting service [Tomcat]","logger_name":"org.apache.catalina.core.StandardService","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.419294-04:00","@version":"1","message":"Starting Servlet engine: [Apache Tomcat/10.1.30]","logger_name":"org.apache.catalina.core.StandardEngine","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.440523-04:00","@version":"1","message":"Initializing Spring embedded WebApplicationContext","logger_name":"org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/]","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.440674-04:00","@version":"1","message":"Root WebApplicationContext: initialization completed in 476 ms","logger_name":"org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.610566-04:00","@version":"1","message":"Tomcat started on port 8080 (http) with context path '/'","logger_name":"org.springframework.boot.web.embedded.tomcat.TomcatWebServer","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.615642-04:00","@version":"1","message":"Started JsonLoggingApplication in 1.039 seconds (process running for 1.364)","logger_name":"guru.springframework.jsonlogging.JsonLoggingApplication","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.617424-04:00","@version":"1","message":"Info log","logger_name":"guru.springframework.jsonlogging.boot.BootStrap","thread_name":"main","level":"INFO","level_value":20000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.617536-04:00","@version":"1","message":"Warning log","logger_name":"guru.springframework.jsonlogging.boot.BootStrap","thread_name":"main","level":"WARN","level_value":30000,"appName":"json-logging"}
{"@timestamp":"2024-10-13T13:01:45.617564-04:00","@version":"1","message":"Error log","logger_name":"guru.springframework.jsonlogging.boot.BootStrap","thread_name":"main","level":"ERROR","level_value":40000,"appName":"json-logging"}

As you can see, it is very easy to add JSON logging to your Spring Boot Application.

This is a very simple example of using JSON logging. The new Structured Logging feature of Spring Boot 3.4.0 is much more robust in capabilities.

About jt

    You May Also Like

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.