항해일지 명사, /lɑɡ bʊk/: 항해의 다른 주요 세부사항과 함께 선박 항해일지의 측정값이 기록되는 책입니다.
Logbook 은 다양한 클라이언트 및 서버 측 기술에 대한 완전한 요청 및 응답 로깅을 가능하게 하는 확장 가능한 Java 라이브러리입니다. 이는 a) 웹 애플리케이션 개발자가 애플리케이션이 받거나 보내는 모든 HTTP 트래픽을 기록할 수 있도록 허용함으로써 b) 나중에 쉽게 유지하고 분석할 수 있는 방식으로 특별한 요구 사항을 충족합니다. 이는 기존 로그 분석, 감사 요구 사항 충족 또는 개별 기록 트래픽 문제 조사에 유용할 수 있습니다.
로그북은 가장 일반적인 설정에 바로 사용할 수 있습니다. 흔하지 않은 애플리케이션과 기술의 경우에도 라이브러리/프레임워크 등을 연결하는 데 필요한 인터페이스를 구현하는 것이 간단해야 합니다. 그것에.
프로젝트에 다음 종속성을 추가합니다.
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-core</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
Spring 5/Spring Boot 2 이전 버전과의 호환성을 위해 다음 가져오기를 추가하세요.
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-servlet</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
Logbook의 추가 모듈/아티팩트는 항상 동일한 버전 번호를 공유합니다.
또는 BOM을 가져올 수도 있습니다.
< dependencyManagement >
< dependencies >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-bom</ artifactId >
< version >${logbook.version}</ version >
< type >pom</ type >
< scope >import</ scope >
</ dependency >
</ dependencies >
</ dependencyManagement >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-core</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-httpclient</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-jaxrs</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-json</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-netty</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-okhttp</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-okhttp2</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-servlet</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-spring-boot-starter</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor-common</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor-client</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor-server</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-logstash</ artifactId >
</ dependency >
요청과 응답을 기록하려면 로그북 로거를 추적 수준으로 구성해야 합니다. Spring Boot 2(Logback 사용)를 사용하면 application.properties
에 다음 줄을 추가하여 이를 수행할 수 있습니다.
logging.level.org.zalando.logbook: TRACE
모든 통합에는 모든 구성을 보유하고 필요한 모든 부분을 함께 연결하는 Logbook
인스턴스가 필요합니다. 모든 기본값을 사용하여 하나를 생성할 수 있습니다.
Logbook logbook = Logbook . create ();
또는 LogbookBuilder
사용하여 사용자 정의 버전을 생성하십시오.
Logbook logbook = Logbook . builder ()
. condition ( new CustomCondition ())
. queryFilter ( new CustomQueryFilter ())
. pathFilter ( new CustomPathFilter ())
. headerFilter ( new CustomHeaderFilter ())
. bodyFilter ( new CustomBodyFilter ())
. requestFilter ( new CustomRequestFilter ())
. responseFilter ( new CustomResponseFilter ())
. sink ( new DefaultSink (
new CustomHttpLogFormatter (),
new CustomHttpLogWriter ()
))
. build ();
로그북은 요청/응답 로깅을 수행하는 방법에 대해 매우 엄격한 전략을 가지고 있었습니다.
이러한 제한 중 일부는 사용자 정의 HttpLogWriter
구현을 통해 완화될 수 있지만 결코 이상적이지는 않습니다.
버전 2.0 Logbook부터 이제 핵심에 전략 패턴이 제공됩니다. 의미를 이해하려면 Strategy
인터페이스의 문서를 읽어보세요.
로그북에는 몇 가지 전략이 내장되어 있습니다:
BodyOnlyIfStatusAtLeastStrategy
StatusAtLeastStrategy
WithoutBodyStrategy
버전 3.4.0부터 Logbook에는 속성 추출기 라는 기능이 탑재되었습니다. 속성은 기본적으로 요청 및/또는 응답에서 추출하여 함께 기록할 수 있는 키/값 쌍의 목록입니다. 이 아이디어는 인증 헤더의 JWT 토큰에서 제목 클레임을 추출하는 기능이 요청된 문제 381에서 시작되었습니다.
AttributeExtractor
인터페이스에는 두 가지 extract
메소드가 있습니다. 하나는 요청에서만 속성을 추출할 수 있는 메소드이고 다른 하나는 요청 및 응답이 모두 가능한 메소드입니다. 둘 다 기본적으로 멋진 Map<String, Object>
인 HttpAttributes
클래스의 인스턴스를 반환합니다. 맵 값은 Object
유형이므로 로그에 의미 있는 방식으로 표시되려면 적절한 toString()
메서드가 있어야 합니다. 또는 로그 포맷터가 자체 직렬화 논리를 구현하여 이 문제를 해결할 수 있습니다. 예를 들어, 내장된 로그 포맷터 JsonHttpLogFormatter
ObjectMapper
사용하여 값을 직렬화합니다.
예는 다음과 같습니다.
final class OriginExtractor implements AttributeExtractor {
@ Override
public HttpAttributes extract ( final HttpRequest request ) {
return HttpAttributes . of ( "origin" , request . getOrigin ());
}
}
그런 다음 이 속성 추출기를 등록하여 로그북을 생성해야 합니다.
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new OriginExtractor ())
. build ();
그러면 요청 로그에 다음과 같은 내용이 포함됩니다.
"attributes":{"origin":"LOCAL"}
고급 예제를 보려면 JwtFirstMatchingClaimExtractor
및 JwtAllMatchingClaimsExtractor
클래스를 살펴보세요. 전자는 요청 JWT 토큰에서 클레임 이름 목록과 일치하는 첫 번째 클레임을 추출합니다. 후자는 요청 JWT 토큰에서 클레임 이름 목록과 일치하는 모든 클레임을 추출합니다.
여러 AttributeExtractor
를 통합해야 하는 경우 CompositeAttributeExtractor
클래스를 사용할 수 있습니다.
final List < AttributeExtractor > extractors = List . of (
extractor1 ,
extractor2 ,
extractor3
);
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new CompositeAttributeExtractor ( extractors ))
. build ();
로그북은 여러 단계로 작동합니다:
각 단계는 사용자 정의에 사용할 수 있는 하나 이상의 인터페이스로 표시됩니다. 모든 단계에는 합리적인 기본값이 있습니다.
HTTP 메시지를 기록하고 해당 본문을 포함하는 것은 비용이 많이 드는 작업이므로 특정 요청에 대한 기록을 비활성화하는 것이 합리적입니다. 일반적인 사용 사례는 로드 밸런서의 상태 확인 요청이나 일반적으로 개발자가 발행한 관리 엔드포인트에 대한 요청을 무시하는 것입니다.
조건을 정의하는 것은 요청(및 해당 응답)을 기록할지 여부를 결정하는 특별한 Predicate
작성하는 것만큼 쉽습니다. 또는 사전 정의된 조건자를 사용하고 결합할 수 있습니다.
Logbook logbook = Logbook . builder ()
. condition ( exclude (
requestTo ( "/health" ),
requestTo ( "/admin/**" ),
contentType ( "application/octet-stream" ),
header ( "X-Secret" , newHashSet ( "1" , "true" ):: contains )))
. build ();
제외 패턴(예: /admin/**
)은 URL의 쿼리 문자열을 고려하지 않고 Ant의 경로 패턴 스타일을 느슨하게 따릅니다.
필터링 의 목표는 HTTP 요청 및 응답의 민감한 특정 부분이 기록되지 않도록 방지하는 것입니다. 여기에는 일반적으로 Authorization 헤더가 포함되지만 특정 일반 텍스트 쿼리 또는 양식 매개변수(예: 비밀번호) 에도 적용될 수 있습니다.
로그북은 다양한 유형의 필터를 지원합니다:
유형 | 다음에서 작동 | 적용대상 | 기본 |
---|---|---|---|
QueryFilter | 쿼리 문자열 | 요구 | access_token |
PathFilter | 길 | 요구 | 해당사항 없음 |
HeaderFilter | 헤더(단일 키-값 쌍) | 둘 다 | Authorization |
BodyFilter | 콘텐츠 유형 및 본문 | 둘 다 | json: access_token 및 refresh_token 형식: client_secret , password 및 refresh_token |
RequestFilter | HttpRequest | 요구 | 바이너리, 멀티파트 및 스트림 본문을 교체합니다. |
ResponseFilter | HttpResponse | 응답 | 바이너리, 멀티파트 및 스트림 본문을 교체합니다. |
QueryFilter
, PathFilter
, HeaderFilter
및 BodyFilter
는 상대적으로 높은 수준이며 모든 경우의 약 90%에서 모든 요구 사항을 충족해야 합니다. 더 복잡한 설정의 경우 낮은 수준 변형, 즉 각각 RequestFilter
및 ResponseFilter
( ForwardingHttpRequest
/ ForwardingHttpResponse
와 함께)로 대체해야 합니다.
다음과 같이 필터를 구성할 수 있습니다.
import static org . zalando . logbook . core . HeaderFilters . authorization ;
import static org . zalando . logbook . core . HeaderFilters . eachHeader ;
import static org . zalando . logbook . core . QueryFilters . accessToken ;
import static org . zalando . logbook . core . QueryFilters . replaceQuery ;
Logbook logbook = Logbook . builder ()
. requestFilter ( RequestFilters . replaceBody ( message -> contentType ( "audio/*" ). test ( message ) ? "mmh mmh mmh mmh" : null ))
. responseFilter ( ResponseFilters . replaceBody ( message -> contentType ( "*/*-stream" ). test ( message ) ? "It just keeps going and going..." : null ))
. queryFilter ( accessToken ())
. queryFilter ( replaceQuery ( "password" , "<secret>" ))
. headerFilter ( authorization ())
. headerFilter ( eachHeader ( "X-Secret" :: equalsIgnoreCase , "<secret>" ))
. build ();
원하는 만큼 필터를 구성할 수 있습니다. 필터는 연속적으로 실행됩니다.
JSON 본문에 JSON 경로 필터링을 적용할 수 있습니다. 다음은 몇 가지 예입니다.
import static org . zalando . logbook . json . JsonPathBodyFilters . jsonPath ;
import static java . util . regex . Pattern . compile ;
Logbook logbook = Logbook . builder ()
. bodyFilter ( jsonPath ( "$.password" ). delete ())
. bodyFilter ( jsonPath ( "$.active" ). replace ( "unknown" ))
. bodyFilter ( jsonPath ( "$.address" ). replace ( "X" ))
. bodyFilter ( jsonPath ( "$.name" ). replace ( compile ( "^( \ w).+" ), "$1." ))
. bodyFilter ( jsonPath ( "$.friends.*.name" ). replace ( compile ( "^( \ w).+" ), "$1." ))
. bodyFilter ( jsonPath ( "$.grades.*" ). replace ( 1.0 ))
. build ();
필터링 적용 전후의 다음 예를 살펴보세요.
{
"id" : 1 ,
"name" : " Alice " ,
"password" : " s3cr3t " ,
"active" : true ,
"address" : " Anhalter Straße 17 13, 67278 Bockenheim an der Weinstraße " ,
"friends" : [
{
"id" : 2 ,
"name" : " Bob "
},
{
"id" : 3 ,
"name" : " Charlie "
}
],
"grades" : {
"Math" : 1.0 ,
"English" : 2.2 ,
"Science" : 1.9 ,
"PE" : 4.0
}
}
{
"id" : 1 ,
"name" : " Alice " ,
"active" : " unknown " ,
"address" : " XXX " ,
"friends" : [
{
"id" : 2 ,
"name" : " B. "
},
{
"id" : 3 ,
"name" : " C. "
}
],
"grades" : {
"Math" : 1.0 ,
"English" : 1.0 ,
"Science" : 1.0 ,
"PE" : 1.0
}
}
로그북은 상관 ID를 사용하여 요청과 응답의 상관 관계를 지정합니다. 이를 통해 일반적으로 로그 파일의 다른 위치에 있는 일치 관련 요청과 응답을 허용합니다.
상관 관계 ID의 기본 구현이 사용 사례에 충분하지 않은 경우 맞춤 구현을 제공할 수 있습니다.
Logbook logbook = Logbook . builder ()
. correlationId ( new CustomCorrelationId ())
. build ();
형식 지정은 기본적으로 요청과 응답이 문자열로 변환되는 방식을 정의합니다. 포맷터는 요청과 응답이 기록되는 위치를 지정하지 않습니다 . 작성자가 해당 작업을 수행합니다.
Logbook에는 HTTP 와 JSON이라는 두 가지 기본 포맷터가 함께 제공됩니다.
HTTP는 DefaultHttpLogFormatter
에서 제공하는 기본 형식 지정 스타일입니다. 주로 프로덕션용이 아닌 로컬 개발 및 디버깅에 사용하도록 설계되었습니다. 이는 JSON만큼 기계에서 쉽게 읽을 수 없기 때문입니다.
Incoming Request: 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b
GET http://example.org/test HTTP/1.1
Accept: application/json
Host: localhost
Content-Type: text/plain
Hello world!
Outgoing Response: 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b
Duration: 25 ms
HTTP/1.1 200
Content-Type: application/json
{ "value" : " Hello world! " }
JSON 은 JsonHttpLogFormatter
에서 제공하는 대체 형식 지정 스타일입니다. HTTP와 달리 주로 프로덕션 용도로 설계되었습니다. 즉, 파서와 로그 소비자가 쉽게 사용할 수 있습니다.
다음 종속성이 필요합니다.
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-json</ artifactId >
</ dependency >
{
"origin" : " remote " ,
"type" : " request " ,
"correlation" : " 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b " ,
"protocol" : " HTTP/1.1 " ,
"sender" : " 127.0.0.1 " ,
"method" : " GET " ,
"uri" : " http://example.org/test " ,
"host" : " example.org " ,
"path" : " /test " ,
"scheme" : " http " ,
"port" : null ,
"headers" : {
"Accept" : [ " application/json " ],
"Content-Type" : [ " text/plain " ]
},
"body" : " Hello world! "
}
{
"origin" : " local " ,
"type" : " response " ,
"correlation" : " 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b " ,
"duration" : 25 ,
"protocol" : " HTTP/1.1 " ,
"status" : 200 ,
"headers" : {
"Content-Type" : [ " text/plain " ]
},
"body" : " Hello world! "
}
참고: application/json
(및 application/*+json
) 유형의 본문은 결과 JSON 트리에 인라인 됩니다. 즉, JSON 응답 본문은 이스케이프되지 않고 문자열로 표시됩니다.
{
"origin" : " local " ,
"type" : " response " ,
"correlation" : " 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b " ,
"duration" : 25 ,
"protocol" : " HTTP/1.1 " ,
"status" : 200 ,
"headers" : {
"Content-Type" : [ " application/json " ]
},
"body" : {
"greeting" : " Hello, world! "
}
}
CLF(Common Log Format)는 서버 로그 파일을 생성할 때 웹 서버에서 사용하는 표준화된 텍스트 파일 형식입니다. 형식은 CommonsLogFormatSink
를 통해 지원됩니다.
185.85.220.253 - - [02/Aug/2019:08:16:41 0000] "GET /search?q=zalando HTTP/1.1" 200 -
ELF(확장 로그 형식)는 로그 파일을 생성할 때 웹 서버에서 사용하는 CLF(공통 로그 형식)와 같은 표준화된 텍스트 파일 형식이지만 ELF 파일은 더 많은 정보와 유연성을 제공합니다. 형식은 ExtendedLogFormatSink
를 통해 지원됩니다. W3C 문서도 참조하세요.
기본 필드:
date time c-ip s-dns cs-method cs-uri-stem cs-uri-query sc-status sc-bytes cs-bytes time-taken cs-protocol cs(User-Agent) cs(Cookie) cs(Referrer)
기본 로그 출력 예:
2019-08-02 08:16:41 185.85.220.253 localhost POST /search ?q=zalando 200 21 20 0.125 HTTP/1.1 "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0" "name=value" "https://example.com/page?q=123"
사용자는 ExtendedLogFormatSink
생성자를 통해 기본 필드를 사용자 정의 필드로 재정의할 수 있습니다.
new ExtendedLogFormatSink ( new DefaultHttpLogWriter (), "date time cs(Custom-Request-Header) sc(Custom-Response-Header)" )
Http 헤더 필드: cs(Any-Header)
및 sc(Any-Header)
의 경우 사용자는 요청에서 추출하려는 헤더를 지정할 수 있습니다.
지원되는 다른 필드는 사용자 정의 필드 표현식에 입력할 수 있는 ExtendedLogFormatSink.Field
값에 나열되어 있습니다.
cURL은 요청을 실행 가능한 cURL
명령으로 렌더링하는 CurlHttpLogFormatter
에서 제공하는 대체 형식 지정 스타일입니다. JSON과 달리 기본적으로 인간을 위해 설계되었습니다.
curl -v -X GET ' http://localhost/test ' -H ' Accept: application/json '
HTTP를 참조하거나 응답에 대한 자체 폴백을 제공하세요.
new CurlHttpLogFormatter ( new JsonHttpLogFormatter ());
Splunk는 요청과 응답을 키-값 쌍으로 렌더링하는 SplunkHttpLogFormatter
에서 제공하는 대체 형식 지정 스타일입니다.
origin=remote type=request correlation=2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b protocol=HTTP/1.1 sender=127.0.0.1 method=POST uri=http://example.org/test host=example.org scheme=http port=null path=/test headers={Accept=[application/json], Content-Type=[text/plain]} body=Hello world!
origin=local type=response correlation=2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b duration=25 protocol=HTTP/1.1 status=200 headers={Content-Type=[text/plain]} body=Hello world!
쓰기는 형식화된 요청과 응답이 기록되는 위치를 정의합니다. Logbook에는 Logger, Stream 및 Chunking의 세 가지 구현이 제공됩니다.
기본적으로 요청 및 응답은 org.zalando.logbook.Logbook
카테고리 및 로그 수준 trace
사용하는 slf4j 로거로 기록됩니다. 이는 다음과 같이 사용자 정의할 수 있습니다.
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new DefaultHttpLogWriter ()
))
. build ();
대체 구현은 PrintStream
(예: System.out
또는 System.err
에 대한 요청과 응답을 기록하는 것입니다. 이는 일반적으로 프로덕션 환경에서 실행하기에 좋지 않은 선택이지만 때로는 단기적인 로컬 개발 및/또는 조사에 유용할 수 있습니다.
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new StreamHttpLogWriter ( System . err )
))
. build ();
ChunkingSink
는 긴 메시지를 더 작은 청크로 분할하고 다른 싱크에 위임하는 동안 개별적으로 작성합니다.
Logbook logbook = Logbook . builder ()
. sink ( new ChunkingSink ( sink , 1000 ))
. build ();
HttpLogFormatter
와 HttpLogWriter
의 조합은 대부분의 사용 사례에 적합하지만 제한 사항이 있습니다. Sink
인터페이스를 직접 구현하면 데이터베이스와 같은 구조화된 영구 저장소에 요청/응답을 쓰는 등 보다 정교한 사용 사례가 가능해집니다.
CompositeSink
를 사용하면 여러 싱크를 하나로 결합할 수 있습니다.
web.xml
파일 중 하나에서 필터 체인에 LogbookFilter
Filter
로 등록해야 합니다(xml 접근 방식은 모든 기본값을 사용하며 구성할 수 없습니다).
< filter >
< filter-name >LogbookFilter</ filter-name >
< filter-class >org.zalando.logbook.servlet.LogbookFilter</ filter-class >
</ filter >
< filter-mapping >
< filter-name >LogbookFilter</ filter-name >
< url-pattern >/*</ url-pattern >
< dispatcher >REQUEST</ dispatcher >
< dispatcher >ASYNC</ dispatcher >
</ filter-mapping >
또는 프로그래밍 방식으로 ServletContext
통해:
context . addFilter ( "LogbookFilter" , new LogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
주의 : ERROR
디스패치는 지원되지 않습니다. REQUEST
또는 ASNYC
디스패치 내에서 오류 응답을 생성하는 것이 좋습니다.
LogbookFilter
는 기본적으로 요청을 다른 요청과 다르지 않은 application/x-www-form-urlencoded
본문으로 처리합니다. 즉, 로그에서 요청 본문을 볼 수 있습니다. 이 접근 방식의 단점은 HttpServletRequest.getParameter*(..)
메서드를 사용할 수 없다는 것입니다. 자세한 내용은 이슈 #94를 참조하세요.
Logbook 1.5.0부터 logbook.servlet.form-request
시스템 속성을 사용하여 Logbook이 이 상황을 처리하는 방법을 정의하는 세 가지 전략 중 하나를 지정할 수 있습니다.
값 | 장점 | 단점 |
---|---|---|
body (기본값) | 본문이 기록되었습니다. | 다운스트림 코드는 getParameter*() 사용할 수 없습니다. |
parameter | 본문이 기록됩니다(그러나 매개변수에서 재구성됨). | 다운스트림 코드는 getInputStream() 사용할 수 없습니다. |
off | 다운스트림 코드는 getInputStream() 또는 getParameter*() 사용 여부를 결정할 수 있습니다. | 본문이 기록되지 않았습니다. |
보안 애플리케이션에는 일반적으로 약간 다른 설정이 필요합니다. 일반적으로 승인되지 않은 요청, 특히 본문을 기록하는 것은 피해야 합니다. 이렇게 하면 공격자가 로그 파일을 빠르게 넘치게 하고 결과적으로 귀중한 디스크 공간을 넘길 수 있기 때문입니다. 애플리케이션이 다른 필터 내에서 인증을 처리한다고 가정하면 다음 두 가지 선택이 있습니다.
보안 필터 뒤에 LogbookFilter
배치하면 이전 설정을 쉽게 달성할 수 있습니다. 후자는 조금 더 정교합니다. 두 개의 LogbookFilter
인스턴스가 필요합니다. 하나는 보안 필터 앞, 다른 하나는 다음과 같습니다.
context . addFilter ( "SecureLogbookFilter" , new SecureLogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
context . addFilter ( "securityFilter" , new SecurityFilter ())
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST ), true , "/*" );
context . addFilter ( "LogbookFilter" , new LogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
첫 번째 로그북 필터는 승인되지 않은 요청 만 기록합니다. 두 번째 필터는 항상 그렇듯이 승인된 요청을 기록합니다.
logbook-httpclient
모듈에는 HttpClient
와 함께 사용할 HttpRequestInterceptor
와 HttpResponseInterceptor
모두 포함되어 있습니다.
CloseableHttpClient client = HttpClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
LogbookHttpResponseInterceptor
는 HttpAsyncClient
와 호환되지 않으므로 응답을 기록하는 다른 방법이 있습니다.
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
logbook-httpclient5
모듈에는 HttpClient
와 함께 사용할 ExecHandler
포함되어 있습니다.
CloseableHttpClient client = HttpClientBuilder . create ()
. addExecInterceptorFirst ( "Logbook" , new LogbookHttpExecHandler ( logbook ))
. build ();
핸들러를 먼저 추가해야 로깅 후에 압축을 하고, 로깅 전에 압축을 푼다.
획기적인 변경을 피하기 위해 HttpClient
와 함께 사용할 수 있는 HttpRequestInterceptor
및 HttpResponseInterceptor
도 있습니다. 이는 압축(또는 다른 ExecHandler)이 사용되지 않는 한 잘 작동합니다.
CloseableHttpClient client = HttpClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addResponseInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
LogbookHttpResponseInterceptor
는 HttpAsyncClient
와 호환되지 않으므로 응답을 기록하는 다른 방법이 있습니다.
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
메모
JAX-RS 2.x 지원
JAX-RS 2.x(레거시) 지원은 Logbook 3.0에서 3.6으로 삭제되었습니다.
Logbook 3.7부터 JAX-RS 2.x 지원이 다시 시작되었습니다.
그러나 적절한 Logbook 모듈을 사용하려면 javax
분류자를 추가해야 합니다.
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-jaxrs</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
또한 클래스 경로에 다음 종속성이 있는지 확인해야 합니다. 기본적으로 logbook-jaxrs
JAX-RS 2.x와 호환되지 않는 jersey-client 3.x
가져옵니다.
logbook-jaxrs
모듈에는 다음이 포함됩니다.
HTTP 요청을 만드는 애플리케이션에 사용되는 LogbookClientFilter
client . register ( new LogbookClientFilter ( logbook ));
HTTP 서버와 함께 사용되는 LogbookServerFilter
resourceConfig . register ( new LogbookServerFilter ( logbook ));
logbook-jdkserver
모듈은 JDK HTTP 서버에 대한 지원을 제공하며 다음을 포함합니다.
내장 서버와 함께 사용할 LogbookFilter
httpServer . createContext ( path , handler ). getFilters (). add ( new LogbookFilter ( logbook ))
logbook-netty
모듈은 다음을 포함합니다:
HttpClient
와 함께 사용할 LogbookClientHandler
:
HttpClient httpClient =
HttpClient . create ()
. doOnConnected (
( connection -> connection . addHandlerLast ( new LogbookClientHandler ( logbook )))
);
HttpServer
와 함께 사용하기 위한 LogbookServerHandler
:
HttpServer httpServer =
HttpServer . create ()
. doOnConnection (
connection -> connection . addHandlerLast ( new LogbookServerHandler ( logbook ))
);
Spring WebFlux 사용자는 다음 옵션 중 하나를 선택할 수 있습니다.
NettyWebServer
생성( HttpServer
전달)NettyServerCustomizer
등록ReactorClientHttpConnector
생성( HttpClient
전달)WebClientCustomizer
등록logbook-spring-webflux
사용 Micronaut 사용자는 Logbook을 Micronaut와 통합하는 방법에 대한 공식 문서를 따를 수 있습니다.
logbook-okhttp2
모듈에는 OkHttpClient
버전 2.x와 함께 사용할 Interceptor
포함되어 있습니다.
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
gzip으로 압축된 응답을 기대한다면 GzipInterceptor
도 추가로 등록해야 합니다. OkHttp에 내장된 투명한 gzip 지원은 로그북이 압축된 바이너리 응답을 기록하도록 강제하는 네트워크 인터셉터 이후에 실행됩니다.
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
client . networkInterceptors (). add ( new GzipInterceptor ());
logbook-okhttp
모듈에는 OkHttpClient
버전 3.x와 함께 사용할 Interceptor
포함되어 있습니다.
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. build ();
gzip으로 압축된 응답을 기대한다면 GzipInterceptor
도 추가로 등록해야 합니다. OkHttp에 내장된 투명한 gzip 지원은 로그북이 압축된 바이너리 응답을 기록하도록 강제하는 네트워크 인터셉터 이후에 실행됩니다.
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. addNetworkInterceptor ( new GzipInterceptor ())
. build ();
logbook-ktor-client
모듈에는 다음이 포함됩니다.
HttpClient
와 함께 사용할 LogbookClient
:
private val client = HttpClient ( CIO ) {
install( LogbookClient ) {
logbook = logbook
}
}
logbook-ktor-server
모듈에는 다음이 포함됩니다.
Application
과 함께 사용할 LogbookServer
:
private val server = embeddedServer( CIO ) {
install( LogbookServer ) {
logbook = logbook
}
}
또는 logbook-ktor-client
및 logbook-ktor-server
모듈을 모두 제공하는 logbook-ktor
사용할 수 있습니다.
logbook-spring
모듈에는 RestTemplate
과 함께 사용할 ClientHttpRequestInterceptor
포함되어 있습니다.
LogbookClientHttpRequestInterceptor interceptor = new LogbookClientHttpRequestInterceptor ( logbook );
RestTemplate restTemplate = new RestTemplate ();
restTemplate . getInterceptors (). add ( interceptor );
Logbook은 Spring Boot 사용자를 위한 편리한 자동 구성과 함께 제공됩니다. 합리적인 기본값을 사용하여 다음 부분을 모두 자동으로 설정합니다.
logbook-core
에 대한 종속성을 선언하는 대신 Spring Boot Starter에 대한 종속성을 선언합니다.
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-spring-boot-starter</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
필요한 경우 모든 빈을 재정의하고 사용자 정의할 수 있습니다. 예를 들면 다음과 같습니다.
@ Bean
public BodyFilter bodyFilter () {
return merge (
defaultValue (),
replaceJsonStringProperty ( singleton ( "secret" ), "XXX" ));
}
가능한 통합 지점 목록을 보려면 LogbookAutoConfiguration
또는 다음 표를 참조하십시오.
유형 | 이름 | 기본 |
---|---|---|
FilterRegistrationBean | secureLogbookFilter | LogbookFilter 기반 |
FilterRegistrationBean | logbookFilter | LogbookFilter 기반 |
Logbook | 조건, 필터, 포맷터 및 작성기에 따라 | |
Predicate<HttpRequest> | requestCondition | 필터 없음; 나중에 logbook.exclude 및 logbook.exclude 와 결합됩니다. |
HeaderFilter | logbook.obfuscate.headers 기반 | |
PathFilter | logbook.obfuscate.paths 기반 | |
QueryFilter | logbook.obfuscate.parameters 기반 | |
BodyFilter | BodyFilters.defaultValue() , 필터링 참조 | |
RequestFilter | RequestFilters.defaultValue() , 필터링 참조 | |
ResponseFilter | ResponseFilters.defaultValue() , 필터링 참조 | |
Strategy | DefaultStrategy | |
AttributeExtractor | NoOpAttributeExtractor | |
Sink | DefaultSink | |
HttpLogFormatter | JsonHttpLogFormatter | |
HttpLogWriter | DefaultHttpLogWriter |
여러 필터가 하나로 병합됩니다.
logbook-spring
에서 자동 구성된 Bean logbook-spring
의 일부 클래스는 자동 구성에 포함됩니다.
다음과 같은 코드를 사용하여 LogbookClientHttpRequestInterceptor
자동 연결할 수 있습니다.
private final RestTemplate restTemplate ;
MyClient ( RestTemplateBuilder builder , LogbookClientHttpRequestInterceptor interceptor ){
this . restTemplate = builder
. additionalInterceptors ( interceptor )
. build ();
}
다음 표에는 사용 가능한 구성이 나와 있습니다(알파벳순으로 정렬).
구성 | 설명 | 기본 |
---|---|---|
logbook.attribute-extractors | type (현재 JwtFirstMatchingClaimExtractor 또는 JwtAllMatchingClaimsExtractor ), claim-names 및 claim-key 와 같은 구성을 포함한 AttributeExtractor 목록입니다. | [] |
logbook.filter.enabled | LogbookFilter 활성화 | true |
logbook.filter.form-request-mode | 양식 요청을 처리하는 방법을 결정합니다. | body |
logbook.filters.body.default-enabled | java.util.ServiceLoader에 의해 수집되는 기본 본문 필터를 활성화/비활성화합니다. | true |
logbook.format.style | 형식 지정 스타일( http , json , curl 또는 splunk ) | json |
logbook.httpclient.decompress-response | gzip으로 인코딩된 본문을 사용하는 HttpClient에 대한 추가 압축 해제 프로세스를 활성화/비활성화합니다(로깅 목적으로만). 이는 추가적인 압축 해제와 성능에 영향을 미칠 수 있음을 의미합니다. | false (비활성화) |
logbook.minimum-status | 로깅을 활성화하기 위한 최소 상태( status-at-least 및 body-only-if-status-at-least ) | 400 |
logbook.obfuscate.headers | 난독화가 필요한 헤더 이름 목록 | [Authorization] |
logbook.obfuscate.json-body-fields | 난독화할 JSON 본문 필드 목록 | [] |
logbook.obfuscate.parameters | 난독화가 필요한 매개변수 이름 목록 | [access_token] |
logbook.obfuscate.paths | 난독화가 필요한 경로 목록입니다. 필터링에서 구문을 확인하세요. | [] |
logbook.obfuscate.replacement | 난독화된 값 대신 사용할 값 | XXX |
logbook.predicate.include | 특정 경로 및 방법만 포함(정의된 경우) | [] |
logbook.predicate.exclude | 특정 경로 및 메서드 제외( logbook.predicate.include 재정의) | [] |
logbook.secure-filter.enabled | SecureLogbookFilter 활성화 | true |
logbook.strategy | 전략( default , status-at-least , body-only-if-status-at-least , without-body ) | default |
logbook.write.chunk-size | 로그 행을 최대 chunk-size 크기의 더 작은 청크로 분할합니다. | 0 (비활성화) |
logbook.write.max-body-size | 본문을 max-body-size 문자까지 자르고 ... 를 추가합니다.logbook.write.max-body-size 값에 관계없이 로그북은 계속해서 전체 본문을 버퍼링합니다. | -1 (비활성화) |
logbook :
predicate :
include :
- path : /api/**
methods :
- GET
- POST
- path : /actuator/**
exclude :
- path : /actuator/health
- path : /api/admin/**
methods :
- POST
filter.enabled : true
secure-filter.enabled : true
format.style : http
strategy : body-only-if-status-at-least
minimum-status : 400
obfuscate :
headers :
- Authorization
- X-Secret
parameters :
- access_token
- password
write :
chunk-size : 1000
attribute-extractors :
- type : JwtFirstMatchingClaimExtractor
claim-names : [ "sub", "subject" ]
claim-key : Principal
- type : JwtAllMatchingClaimsExtractor
claim-names : [ "sub", "iat" ]
기본 Logback 구성의 경우
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
LogstashLogbackSink
로 로그북 구성
HttpLogFormatter formatter = new JsonHttpLogFormatter();
LogstashLogbackSink sink = new LogstashLogbackSink(formatter);
다음과 같은 출력의 경우
{
"@timestamp" : "2019-03-08T09:37:46.239+01:00",
"@version" : "1",
"message" : "GET http://localhost/test?limit=1",
"logger_name" : "org.zalando.logbook.Logbook",
"thread_name" : "main",
"level" : "TRACE",
"level_value" : 5000,
"http" : {
// logbook request/response contents
}
}
LogstashLogbackSink
특정 수준으로 초기화하여 기본 로깅 수준을 유연하게 사용자 지정할 수 있습니다. 예를 들어:
LogstashLogbackSink sink = new LogstashLogbackSink(formatter, Level.INFO);
getWriter
및/또는 getParameter*()
사용하는 다운스트림 코드를 방해합니다. 자세한 내용은 서블릿을 참조하세요.ERROR
디스패치를 지원하지 않습니다 . 오류 응답을 생성하는 데 사용하지 않는 것이 좋습니다. 질문, 우려 사항, 버그 보고서 등이 있는 경우 이 저장소의 이슈 트래커에 문제를 제출하세요.
기여하려면 끌어오기 요청을 하고 추가 또는 변경 사항에 대한 간단한 설명(1~2문장)을 추가하세요. 자세한 내용은 기여 가이드라인을 확인하세요.
Nelson 시대의 3개의 돛대를 갖춘 6급 호위함의 복제품인 Grand Turk - JoJan의 일지 및 차트 는 크리에이티브 커먼즈(저작자표시-동일조건변경허락 3.0 Unported)에 따라 라이센스가 부여됩니다.