API를 개발하다 보면 HTTP 요청과 응답을 로깅해야 할 때가 있다. 단순히
log.info()로 남기기에는 매번 코드에 직접 작성해야 하고, 민감 정보 마스킹이나 환경별 설정 분리까지 고려하면 상당히 번거로워진다. 이를 해결할 수 있는 방법을 찾다가 Zalando에서 만든 Logbook이라는 라이브러리를 알게 되었고, 설정만으로 HTTP 통신을 자동으로 로깅할 수 있었다. Logbook에 대해 정리하고자 한다!

Zalando에서 만든 HTTP 요청/응답 전용 로깅 라이브러리이다. 일반 로깅과 달리 HTTP 요청/응답에 특화되어 있어 요청과 응답을 분리해서 로깅할 수 있다. 설정만 하면 모든 HTTP 통신이 자동으로 로깅되며, REST API, WebClient, RestTemplate, Feign 등과 연동이 가능하다.
| 컴포넌트 | 역할 | 상세 |
|---|---|---|
| Condition | 어떤 요청을 로깅할지 결정 | URL 패턴, HTTP 메서드, 헤더 기반 필터링 |
| Sink | 로그를 실제로 출력 | SLF4J, Apache Commons Logging 등과 연동 |
| Strategy | 요청/응답 처리 방식을 결정 | default, status-at-least, body-only-if-status-at-least, without-body |
| Strategy | 설명 |
|---|---|
default | 모든 요청/응답 로깅 |
status-at-least | 특정 상태 코드 이상만 로깅 |
body-only-if-status-at-least | 특정 상태 코드 이상일 때만 body 포함 |
without-body | body 제외하고 로깅 |
Logbook은 요청이 들어오면 다음 4단계를 거쳐 로깅을 수행한다.
| 설정 | 설명 |
|---|---|
logbook.filter.enabled | LogbookFilter 사용 여부 |
logbook.format.style | 로그 출력 형식 (http, json, curl, splunk) |
logbook.minimum-status | 특정 HTTP 상태 코드 이상의 응답부터 로깅 ex) 400 설정 시 4xx, 5xx만 로깅 |
logbook.obfuscate.headers | 마스킹할 헤더 목록 ex) Authorization, Cookie |
logbook.obfuscate.parameters | 마스킹할 URL 쿼리 파라미터 목록 ex) password, access_token |
logbook.obfuscate.json-body-fields | 마스킹할 JSON 바디 필드 목록 ex) password, email |
logbook.obfuscate.replacement | 마스킹된 부분에 표시할 문자 (기본값 XXX) |
logbook.predicate.include | 특정 경로만 로깅 |
logbook.predicate.exclude | 특정 경로는 로깅에서 제외 |
logbook.write.chunk-size | 로그를 작은 조각으로 나눌 크기 (기본 8192byte) |
logbook.write.max-body-size | 바디가 너무 클 때 잘라낼 최대 글자수 (기본 8192byte) |
include vs exclude : exclude 설정이 include 설정보다 우선적으로 적용된다.filter.enabled : false로 설정하면 모든 로깅 필터가 비활성화된다.secure-filter.enabled : false로 설정하면 obfuscate 마스킹이 무시된다.마스킹 대상에 따라 설정 위치가 다르다. 각 설정의 대상과 예시를 정리하면 다음과 같다.
| 설정 | 대상 | 예시 |
|---|---|---|
obfuscate.headers | HTTP 헤더 값 | Authorization: *** |
obfuscate.parameters | URL 쿼리 파라미터 값 | ?password=*** |
obfuscate.json-body-fields | JSON 바디의 필드 값 (yml) | {"password":"***"} |
BodyFilter Bean | JSON 바디의 필드 값 (Java) | {"email":"***"} |
obfuscate.json-body-fields는 yml 설정만으로 간단히 적용 가능하지만, logbook-json 모듈의 JsonBodyFilters를 사용한 BodyFilter Bean 등록 방식이 더 세밀한 제어가 가능하다.
// Logbook - HTTP 요청/응답 로깅
implementation 'org.zalando:logbook-spring-boot-starter:3.9.0'
implementation 'org.zalando:logbook-json:3.9.0'
| 의존성 | 설명 |
|---|---|
logbook-spring-boot-starter | Spring Boot 자동 설정 및 기본 로깅 기능을 제공한다 |
logbook-json | JsonBodyFilters 등 JSON 바디 필터링 기능을 제공한다. logbook-spring-boot-starter에 자동으로 포함되지 않으므로 명시적으로 추가해야 한다 |
logbook:
filter:
enabled: true
obfuscate:
replacement: "***"
headers:
- Authorization
- Cookie
- Set-Cookie
parameters:
- password
- access_token
- refresh_token
- code
---
spring:
config:
activate:
on-profile: local
logbook:
format:
style: http
predicate:
exclude:
- path: /actuator/**
- path: /swagger-ui/**
- path: /v3/api-docs/**
- path: /swagger-resources/**
- path: /favicon.ico
logging:
level:
org.zalando.logbook: TRACE
---
spring:
config:
activate:
on-profile: dev
logbook:
format:
style: json
predicate:
exclude:
- path: /actuator/**
- path: /swagger-ui/**
- path: /v3/api-docs/**
- path: /swagger-resources/**
- path: /favicon.ico
logging:
level:
org.zalando.logbook: TRACE
---
spring:
config:
activate:
on-profile: prod
logbook:
format:
style: json
minimum-status: 400
write:
max-body-size: 2048
predicate:
exclude:
- path: /actuator/**
- path: /swagger-ui/**
- path: /v3/api-docs/**
- path: /swagger-resources/**
- path: /favicon.ico
- path: /excel-upload/**
logging:
level:
org.zalando.logbook: TRACE
공통 영역에 마스킹 설정을 두어 모든 프로필에서 동일하게 적용되도록 하고, 각 프로필별로 포맷과 로깅 범위를 다르게 설정하였다.
| 환경 | 포맷 | 로깅 범위 | 특이사항 |
|---|---|---|---|
| 공통 (default) | - | 전체 | 마스킹 설정 (헤더, 파라미터) |
| local | http | 전체 | 가독성 좋은 HTTP 포맷 |
| dev | json | 전체 | 구조화된 JSON 포맷 |
| prod | json | 400 이상만 | minimum-status: 400, max-body-size: 2048 제한 |
logging.level.org.zalando.logbook: TRACE 설정이 있어야 HTTP 요청/응답 로깅이 동작한다.
prod 환경에서 minimum-status: 400을 설정하면 정상 응답(2xx, 3xx)은 로깅하지 않아 성능 부담을 줄일 수 있다. 또한, prod 환경에서 max-body-size: 2048을 설정하면 대용량 응답 바디가 잘려서 로깅되어 로그 저장 비용을 절감할 수 있다.
@Configuration
public class LogbookConfig {
@Bean
public BodyFilter bodyFilter() {
return JsonBodyFilters.replaceJsonStringProperty(
Set.of(
"password",
"accessToken", "refreshToken",
"code",
"email"
),
"***"
);
}
}
Logbook 전체를 Bean으로 등록하지 않고, BodyFilter만 Bean으로 등록하면 yml 설정과 커스텀 로직을 동시에 사용할 수 있다. JsonBodyFilters.replaceJsonStringProperty()는 logbook-json 모듈에 포함되어 있으며, JSON 바디 내 지정된 필드 값을 ***로 마스킹한다.
Logbook 전체를 Bean으로 만들면 (Logbook.builder()...build()) application.yml에 정의한 설정이 모두 무시된다. 커스텀 로직이 필요한 컴포넌트(BodyFilter, HttpLogWriter 등)만 개별 Bean으로 등록해야 yml 설정과 공존할 수 있다.
| 마스킹 대상 | 설정 위치 | 설정 방법 |
|---|---|---|
| HTTP 헤더 (Authorization, Cookie 등) | yml | obfuscate.headers |
| URL 쿼리 파라미터 (password, token 등) | yml | obfuscate.parameters |
| JSON 바디 필드 (password, email 등) | Java Config | BodyFilter Bean + JsonBodyFilters |
2026-02-16T18:15:56.915+09:00 TRACE 79267 --- [room-in-us] [nio-8090-exec-1] org.zalando.logbook.Logbook : Incoming Request: c3f73321e96883eb
Remote: 0:0:0:0:0:0:0:1
POST http://localhost:8090/login HTTP/1.1
accept: */*
accept-encoding: gzip, deflate, br, zstd
accept-language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
connection: keep-alive
content-length: 65
content-type: application/json
cookie: ***
host: localhost:8090
origin: http://localhost:8090
referer: http://localhost:8090/api/swagger-ui/index.html
sec-ch-ua: "Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-origin
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
{"email":"***","password":"***"}
요청의 cookie 헤더가 ***로 마스킹되고, 바디의 email, password 필드도 ***로 마스킹된 것을 확인할 수 있다.
2026-02-16T18:15:57.255+09:00 TRACE 79267 --- [room-in-us] [nio-8090-exec-1] org.zalando.logbook.Logbook : Outgoing Response: c3f73321e96883eb
Duration: 346 ms
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Type: application/json
Date: Mon, 16 Feb 2026 09:15:57 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
Set-Cookie: ***, ***
Transfer-Encoding: chunked
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
{"memberId":1,"accessToken":"***","refreshToken":"***"}
응답 헤더의 Set-Cookie와 바디의 accessToken, refreshToken 필드가 ***로 마스킹된 것을 확인할 수 있다.
Logbook을 통해 설정만으로 HTTP 요청/응답을 자동으로 로깅할 수 있었다. 특히 환경별로 로깅 전략을 다르게 가져갈 수 있어, local에서는 가독성 좋은 HTTP 포맷으로 전체 로깅을 하고, prod에서는 JSON 포맷으로 에러 응답만 로깅하는 식으로 분리할 수 있었다. 라이브러리를 통해 편하게 로그를 관리할 수 있다는 점이 큰 장점으로 느껴졌다!
Logbook - GitHub
Introducing Zalando Logbook and How to Integrate It with Spring Boot - NashTech Blog