Logbook is an extensible library to enable request and response logging for different client- and server-side technologies. It comes with a core module logbook-core
and specific modules per framework, e.g. logbook-servlet
for Servlet 3.0 environments and logbook-spring
for applications using Spring's RestTemplate
.
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook</artifactId>
<version>${logbook.version}</version>
</dependency>
All integrations require an instance of Logbook
which holds all configuration and wires all necessary parts together. You can either create one using all the defaults:
Logbook logbook = Logbook.create()
or create a customized version using the LogbookBuilder
:
Logbook logbook = Logbook.builder()
.formatter(new CustomHttpLogFormatter())
.writer(new CustomHttpLogWriter())
.build();
Logbook works in three phases:
- Obfuscation,
- Formatting and
- Writing
Each phase is represented by one or more interfaces that can be used for customization and every phase has a sensible default:
The goal of Obfuscation is to prevent certain sensitive parts of HTTP requests and responses to be logged. This usually includes the Authorization header but could also apply to certain plaintext query or form parameters, e.g. password.
Logbook differentiates between Obfuscator
(for headers and query parameters) and BodyObfuscator
. The default behaviour for all of them is to not obfuscate at all.
You can use predefined obfuscators:
Logbook logbook = Logbook.builder()
// will replace the Authorization header value with XXX
.headerObfuscator(authorization())
.build();
or create custom ones:
Logbook logbook = Logbook.builder()
.parameterObfuscator(obfuscate("password"::equals, "XXX"))
.build();
or combine them:
Logbook logbook = Logbook.builder()
.headerObfuscator(compound(
authorization(),
obfuscate("X-Secret"::equals, "XXX")))
.build();
Formatting defines how requests and responses will be transformed to strings basically. Formatters do not specify where requests and responses are logged to, that's the work of writers.
Logbook comes with two different formatters by default - HTTP and JSON:
HTTP is the default formatting style, is provided by the DefaultHttpLogFormatter
and is primarily designed to be used for local development and debugging. Since it's harder to read by machines this is usually not meant to be used in production.
GET /test HTTP/1.1
Accept: application/json
Host: localhost
Content-Type: text/plain
Hello world!
HTTP/1.1 200
Content-Type: application/json
{"value":"Hello world!"}
JSON is an alternative formatting style, is provided by the JsonHttpLogFormatter
and is primarily designed to be used for production since it's easily consumed by parsers and log consumers.
{
"sender": "127.0.0.1",
"method": "GET",
"path": "/test",
"headers": {
"Accept": "application/json",
"Content-Type": "text/plain"
},
"params": {
"limit": "1000"
},
"body": "Hello world!"
}
{
"status": 200,
"headers": {
"Content-Type": "text/plain"
},
"body": "Hello world!"
}
Writing defines where formatted requests and responses are written to. Logback comes with two implementations:
By default requests and responses are logged using a slf4j logger that uses the org.zalando.logbook.Logbook
category and the log level trace
. This can be customized though:
Logbook logbook = Logbook.builder()
.writer(new DefaultHttpLogWriter(
LoggerFactory.getLogger("http.wire-log"),
Level.DEBUG))
.build();
An alternative implementation is logging requests and responses to a PrintStream
, e.g. System.out
or System.err
. This is usually a bad choice for running in production, but might be used for short-term local development and/or investigations.
Logbook logbook = Logbook.builder()
.writer(new StreamHttpLogWriter(System.err))
.build();
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-servlet</artifactId>
<version>${logbook.version}</version>
</dependency>
You have to register the LogbookFilter
as a Filter
in your filter chain.
Either in your web.xml
file:
<filter>
<filter-name>LogbookFilter</filter-name>
<filter-class>org.zalando.logbook.LogbookFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LogbookFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
(Please note that the xml approach will use all the defaults and is not configurable.)
Or programmatically via the ServletContext
:
context.addFilter("LogbookFilter", new LogbookFilter(logbook))
.addMappingForUrlPatterns(EnumSet.of(REQUEST, ASYNC, ERROR), true, "/*");
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-spring</artifactId>
<version>${logbook.version}</version>
</dependency>
The logbook-spring
module contains a ClientHttpRequestInterceptor
to be used with Spring's RestTemplate
:
RestTemplate template = new RestTemplate();
template.setInterceptors(asList(new LogbookClientHttpRequestInterceptor(logbook)));
Copyright [2015] Zalando SE
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.