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
viaSLF4J
) - 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>
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>
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;
}
}
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.
Top comments (0)