1. Overview

HTTP API requests are part of most applications now. Logbook is an extensible Java library to enable complete request and response logging for different client and server-side technologies. It allows developers to log any HTTP traffic an application receives or sends. This can be used for log analysis, auditing, or investigating traffic issues.

In this article, let’s go through the integration of the Logbook library with a Spring Boot application.

2. Dependencies

To use the Logbook library with Spring Boot, we add the following dependency to the project:

<dependency>
    <groupId>org.zalando</groupId>
    <artifactId>logbook-spring-boot-starter</artifactId>
    <version>3.9.0</version>
</dependency>

We can find the latest version of the Logbook library in Maven Central.

3. Configuration

Logbook works with logback logging in the Spring Boot application. We need to add configuration to the logback-spring.xml and the application.properties files.

Once we add the Logbook library to pom.xml, the Logbook library is autoconfigured with Spring Boot. Let’s add a log level to the application.properties file:

logging.level.org.zalando.logbook.Logbook=TRACE

Log level TRACE enables the logging of HTTP requests and responses.

Also, we add the Logbook configuration in the logback-spring.xml file:

<logger name="org.zalando.logbook" level="INFO" additivity="false">
    <appender-ref ref="RollingFile"/>
</logger>

Once this is added, we can run the application with an HTTP request. After each HTTP request call, the Logbook library logs the request and the response to the log file path specified in the logback-spring.xml configuration:

11:08:14.737 [http-nio-8083-exec-10] TRACE org.zalando.logbook.Logbook - Incoming Request: eac2321df47c4414
Remote: 0:0:0:0:0:0:0:1
GET http://localhost:8083/api/hello?name=James HTTP/1.1
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
accept-encoding: gzip, deflate, br, zstd
...
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36
11:08:14.741 [http-nio-8083-exec-10] TRACE org.zalando.logbook.Logbook - Outgoing Response: eac2321df47c4414
Duration: 4 ms
HTTP/1.1 200 OK
...
Date: Tue, 18 Jun 2024 05:38:14 GMT
Keep-Alive: timeout=60

Hello, James!

We’ve seen how the Logbook library is integrated with Spring Boot using minimal configuration. However, this is basic request and response logging. Let’s look into further configurations in the next subsections.

4. Filtering and Formatting

We can declare the Logbook library configuration bean:

@Bean
public Logbook logbook() {
    Logbook logbook = Logbook.builder()
      .condition(Conditions.exclude(Conditions.requestTo("/api/welcome"), 
        Conditions.contentType("application/octet-stream"), 
        Conditions.header("X-Secret", "true")))
      .sink(new DefaultSink(new DefaultHttpLogFormatter(), new DefaultHttpLogWriter()))
      .build();
    return logbook;
}

In the above code example, we declare the Logbook bean so that Spring Boot picks it up for loading configuration.

Now, we’ve specified conditions while building the Logbook bean. The request mapping specified in the exclude() method is excluded from logging. In this case, the Logbook library won’t log requests or responses for API mapped to path “*/api/welcome*“. Likewise, we’ve put filters on requests having content type using the contentType() method, and header using the header() method.

Similarly, HTTP APIs that should be included should be specified in the include() method. If we don’t use the include() method*,* Logbook logs all requests except the ones mentioned in the exclude() method.**

We should set the filter property in the application.properties file for this filter configuration to work. The property logbook.filter.enabled should be set to true:

logbook.filter.enabled=true

The Logbook library logs requests and responses with an sl4j logger that uses the org.zalando.logbook.Logbook category and the log level trace by default:

sink(new DefaultSink(
  new DefaultHttpLogFormatter(),
  new DefaultHttpLogWriter()
))

Then, we change the encoder under the appender in logback-spring.xml. The log stash encoder enables LogstashLogbackSink:

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    ... 
    <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>

Now we declare LogstashLogbackSink and add it to the Logbook object builder:

HttpLogFormatter formatter = new JsonHttpLogFormatter(); 
LogstashLogbackSink logstashsink = new LogstashLogbackSink(formatter);
Logbook logbook = Logbook.builder()
  .sink(logstashsink)
  .build();

Here we’ve used JsonHttpLogFormatter with LogstashLogbackSink, This customization prints logs in the JSON format:

{
    "@timestamp": "2024-06-07T16:46:24.5673233+05:30",
    "@version": "1",
    "message": "200 OK GET http://localhost:8083/api/hello?name=john",
    "logger_name": "org.zalando.logbook.Logbook",
    "thread_name": "http-nio-8083-exec-6",
    "level": "TRACE",
    "http":  {
        ...
        "Content-Length": [
            "12"
        ],
        ...
        "body": "Hello, john!"
    }
}

The JSON log is printed in single lines; we’ve formatted it here for better readability.

We can change the log level while declaring the LogstashLogbackSink object:

LogstashLogbackSink logstashsink = new LogstashLogbackSink(formatter, Level.INFO);

We can also use SplunkHttpLogFormatter with the Logbook sinks. It prints the log in key-value format:

origin=remote ... method=GET uri=http://localhost:8083/api/hello?name=John host=localhost path=/api/hello ...

5.3. Composite Sink

Combining multiple sinks we can form a CompositeSink:

CompositeSink compsink = new CompositeSink(Arrays.asList(logstashsink, new CommonsLogFormatSink(new DefaultHttpLogWriter())));
Logbook logbook = Logbook.builder()
  .sink(compsink)
  .build();

This configuration logs request details using all the sinks specified in the composite sink. Here, the Logbook logs the request by combining two sinks:

... "message":"GET http://localhost:8083/api/hello?name=John",... uri":"http://localhost:8083/api/hello?name=John",...
... "message":"200 OK GET http://localhost:8083/api/hello?name=John",.."headers":{"Connection":["keep-alive"],...

6. Conclusion

In this article, we’ve learned how to integrate the Logbook library with Spring Boot using minimal configuration. Also, about filtering the request paths using exclude() and include() methods. We’ve also learned about customizing log format as needed using Sink implementations like LogstashLogbackSink with formatters like JsonHttpLogFormatter.

As always the source code examples are available over on GitHub.