Application and Webserver Logging in Spring Boot 3.1
Tags: java, springboot, logs
Published: 2023-09-08 18:15:35

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`:
```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>
```

### A Configuration File

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

This example is very close to the default pattern from Spring Boot:
```xml
<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>
```

### A Bean
```java
@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;
    }
}
```
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](https://logging.apache.org/log4j/2.x/log4j-slf4j-impl.html) `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](https://docs.spring.io/spring-framework/reference/core/spring-jcl.html#:~:text=of%20the%20Log4j%202.x%20API%20and%20the%20SLF4J%201.7%20API%20in%20the%20classpath%20and%20uses%20the%20first%20one%20of%20those%20found%20as%20the%20logging%20implementation%2C%20falling%20back%20to%20the%20Java%20platform%E2%80%99s%20core%20logging%20facilities%20(also%20known%20as%20JUL%20or%20java.util.logging)%20if%20neither%20Log4j%202.x%20nor%20SLF4J%20is%20available.) 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](https://projectlombok.org/), you can use its [logging annotations](https://projectlombok.org/features/log) 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](https://logback.qos.ch/access.html#:~:text=%3C/Ref%3E-,Logback%2Daccess%20configuration,-Although%20similar%2C%20the).