Application and Webserver Logging in Spring Boot 3.1

Matthew Gale - Sep 8 '23 - - Dev Community

Whenever I start a new Spring Boot project, I have to relearn how logging works to configure it. After so many years and new versions of frameworks, I decided to go deep and break down logging in Spring boot in the modern age and tease apart the various logging libraries and configurations to set the record straight.

In a run-of-the-mill Spring boot app, you'd like:

  • Application logs for Spring Boot 3.1 (using the default Logback via SLF4J)
  • Webserver access logs (using the default embedded Tomcat v10)
  • All logs going to the same sink (locally, that’s STDOUT)

The minimal configuration you need:

Dependencies

In your pom.xml:

<dependencies>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>1.4.11</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.4.11</version>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

A Configuration File

In src/main/resources/ create a logback-access.xml.

This example is very close to the default pattern from Spring Boot:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.access.PatternLayoutEncoder">
            <pattern>%t{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}  INFO %-5.5(0) --- [%15.15I] %-40.40(org.apache.tomcat) : %requestURL %statusCode</pattern>
        </encoder>
    </appender>

    <appender-ref ref="STDOUT" />
</configuration>
Enter fullscreen mode Exit fullscreen mode

A Bean

@Configuration
public class AppConfig {

    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
        LogbackValve logbackValve = new LogbackValve();
        logbackValve.setFilename("logback-access.xml");
        tomcatServletWebServerFactory.addContextValves(logbackValve);
        return tomcatServletWebServerFactory;
    }
}
Enter fullscreen mode Exit fullscreen mode

With this together, Tomcat access logs will log alongside your application.

But what even is all of this? Why is this so cumbersome?

To refresh, java has a TON of logging frameworks. To name a few popular ones:

  • java.util.logging (aka JUL)
  • Log4j
  • Logback
  • Log4j2

Which one you pick is up to you, what’s important here is how Spring Boot detects what you want and uses it.

SLF4J (Simple Logging Facade) is a popular abstraction library over the most used logging frameworks- it allows you (or in this case, Spring) to interact with a facade instead of the underlying logging library, so you can pick whichever logging library appeals to you at no additional development effort.

However: Log4j2 has diverged from the contract SLF4J expects, and without an adapter SLF4J can’t service Log4j2.

A small detail to mention: the authors of SLF4J are also the authors of Logback, and so the usage of them together is often assumed, unless otherwise stated.

Logging in Spring

To make configuration easier, Spring has created a library called spring-jcl (Jakarta Commons Logging) that searches your classpath and attempts to automatically configure your logging based on what it finds. spring-jcl has a preferred order to which logging frameworks it will select depending on what it finds, but the Spring Boot starter is set up to use Logback via SLF4J. You can use any logger you’d like though, you just have to configure it.

If you use lombok, you can use its logging annotations to choose which logger you’d like use in any given class. For example, in the default config for Spring Boot, you can use @Slf4j to use Logback. If you’re configured for Log4j via SLF4J, you could use @Slf4j or use @Log4j if you’d like to use the bare logger.

As an aside, recall that because SLF4J and Logback are often synonymous, there’s no need for a @Logback helper from lombok- @Slf4j would usually use Logback anyway!

Logging in Tomcat

Because we embed Tomcat in our applications now, it’s easy to forget that Tomcat is a web server with a servlet container, so it’s actions and concerns are very different than our application code.

For example, if you attempt to visit an unknown route (resulting in a 404), that request never makes it to the application container. Tomcat is aware of the known routes, and rejects the request before it makes it to your application. That 404 is recorded in Tomcat’s access logs, which by default go to a file- so if we want access logs alongside our application logs, we need to configure Tomcat to use a different appender, which means informing it about which logging framework we’re using so it can direct its logs there.

Tomcat is configurable in that during initialization, you can pass it a logging pipeline (a series of org.apache.catalina.Valve s) to handle your logs, and in our case, direct the flow of logs to an appender we configure and control.

Sticking with the default Logback for Spring Boot, a project exists from the Logback team called ogback-access that comes with a valve (ch.qos.logback.access.tomcat.LogbackValve) we can use to direct the log stream. A quirk to be aware of is that while logback-access might have many classnames identical to ones in logback-core, their behaviour is different and they are not interchangeable: logback-access is more HTTP specific. The documentation for logback-access is here.

. . .