Sometimes it can be handy to have a Spring Boot app log http requests to its embedded web server. Here is a simple way to achieve this, with minimal dependencies. The examples apply only when using default Apache Tomcat embedded web server and Logback as logging implementation.
1. Add dependency logback-access
to your project:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
</dependency>
2. Add Logback access log configuration file to root of classpath:src/main/resources/logback-access.xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appender name="access_stdout"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>common</pattern>
</encoder>
</appender>
<appender-ref ref="access_stdout"/>
</configuration>
3. Add a @Configuration
class which integrates Logback into Tomcat (Kotlin in this example):
import ch.qos.logback.access.tomcat.LogbackValve
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.nio.file.Files
@Configuration
open class AccessLogConfiguration {
@Bean
fun addLogbackAccessValve() = TomcatContextCustomizer { context ->
javaClass.getResourceAsStream("/logback-access.xml").use {
Files.createDirectories((context.catalinaBase.toPath()
.resolve(LogbackValve.DEFAULT_CONFIG_FILE)).parent)
Files.copy(it, context.catalinaBase.toPath()
.resolve(LogbackValve.DEFAULT_CONFIG_FILE))
}
LogbackValve().let {
it.isQuiet = true
context.pipeline.addValve(it)
}
}
}
Here we copy the Logback access configuration file into Tomcat embedded web server runtime dir. This circumvents an issue with LogbackValve
being initialized in a different class loader context by Tomcat, where it is not able to resolve its configuration file in the Spring Boot app main classpath. If it’s not working, set isQuiet
to false
to debug.
Now your Spring Boot app logs http requests through the console appender in a typical format:
127.0.0.1 - - [20/feb./2021:22:16:54 +0100] "GET / HTTP/1.1" 200 332
Getting Logstash-compatible JSON-output
In case you use Kibana and need structured log formatting, you can easily achieve this now. First ensure you have the dependencynet.logstash.logback:logstash-logback-encoder
added to your build. Then simply use a slightly differentlogback-access.xml
configuration file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appender name="access_stdout"
class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashAccessEncoder"/>
</appender>
<appender-ref ref="access_stdout"/>
</configuration>
Now your access log messages will be structured like this:
{
"@timestamp": "2021-02-21T17:38:30.813+01:00",
"@version": "1",
"message": "127.0.0.1 - - [2021-02-21T17:38:30.813+01:00] \"GET / HTTP/1.1\" 200 332",
"method": "GET",
"protocol": "HTTP/1.1",
"status_code": 200,
"requested_url": "GET / HTTP/1.1",
"requested_uri": "/",
"remote_host": "127.0.0.1",
"content_length": 332,
"elapsed_time": 47
}