航海日誌名詞,/lɑɡ bʊk/:記錄船舶航海日誌測量結果以及航程其他重要細節的書。
Logbook是一個可擴展的 Java 程式庫,可為不同的用戶端和伺服器端技術啟用完整的請求和回應日誌記錄。它透過以下方式滿足了特殊需求:a) 允許 Web 應用程式開發人員記錄應用程式接收或發送的任何 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 >
日誌的其他模組/工件始終共用相同的版本號。
或者,您可以匯入我們的物料清單...
< 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 配備了名為Attribute Extractor 的功能。屬性基本上是可以從請求和/或回應中提取並用它們記錄的鍵/值對清單。這個想法源自於問題 381,其中請求一個功能從授權標頭中的 JWT 令牌中提取主題聲明。
AttributeExtractor
介面有兩種extract
方法:一種只能從請求中提取屬性,另一種可以同時提取請求和回應。兩者都會傳回HttpAttributes
類別的實例,它基本上是一個奇特的Map<String, Object>
。請注意,由於映射值的類型為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/**
,鬆散地遵循 Ant 的路徑模式風格,而不考慮 URL 的查詢字串。
過濾的目標是防止記錄 HTTP 請求和回應的某些敏感部分。這通常包括授權標頭,但也可以應用於某些純文字查詢或表單參數 - 例如密碼。
日誌支援不同類型的過濾器:
類型 | 運行於 | 適用於 | 預設 |
---|---|---|---|
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) 是 Web 伺服器在產生伺服器日誌檔案時使用的標準化文字檔案格式。此格式透過CommonsLogFormatSink
支援:
185.85.220.253 - - [02/Aug/2019:08:16:41 0000] "GET /search?q=zalando HTTP/1.1" 200 -
擴充日誌格式 (ELF) 是一種標準化文字檔案格式,與通用日誌格式 (CLF) 一樣,Web 伺服器在產生日誌檔案時使用該格式,但 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是一種替代格式樣式,由CurlHttpLogFormatter
提供,它將請求呈現為可執行的cURL
命令。與 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。
預設情況下,請求和回應是使用slf4j記錄器記錄的,該記錄器使用org.zalando.logbook.Logbook
類別和日誌等級trace
。這可以客製化:
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
將多個接收器合併為一個。
您必須將LogbookFilter
註冊為過濾器鏈中的Filter
- 或在您的web.xml
檔案中(請注意,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 ();
應先添加Handler,以便在記錄後進行壓縮,並在記錄前進行解壓。
為了避免重大更改,還有一個HttpRequestInterceptor
和HttpResponseInterceptor
與HttpClient
一起使用,只要不使用壓縮(或其他 ExecHandlers),它們就可以正常工作:
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
Logbook 3.0 至 3.6 中刪除了 JAX-RS 2.x(舊版)支援。
從 Logbook 3.7 開始,JAX-RS 2.x 支援又回來了。
但是,您需要新增javax
分類器才能使用正確的日誌模組:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-jaxrs</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
您還應該確保以下依賴項位於您的類別路徑上。預設情況下, logbook-jaxrs
導入jersey-client 3.x
,它與 JAX-RS 2.x 不相容:
logbook-jaxrs
模組包含:
LogbookClientFilter
用於發出 HTTP 請求的應用程式
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
,它附帶logbook-ktor-client
和logbook-ktor-server
模組。
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 都可以被覆蓋和定制,例如:
@ 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 | AttributeExtractors 列表,包括type (目前為JwtFirstMatchingClaimExtractor 或JwtAllMatchingClaimsExtractor )、 claim-names 和claim-key 等配置。 | [] |
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*()
幹擾下游程式碼。有關詳細信息,請參閱 Servlet。ERROR
調度。強烈建議您不要使用它來產生錯誤回應。 如果您有疑問、疑慮、錯誤報告等,請在此儲存庫的問題追蹤器中提交問題。
要做出貢獻,只需提出拉取請求並添加有關添加或更改的簡短描述(1-2 句話)。有關更多詳細信息,請查看貢獻指南。
Grand Turk 是納爾遜時代一艘三桅六級護衛艦的複製品 - JoJan 的航海日誌和圖表已獲得知識共享許可(署名-相同方式共享 3.0 未移植)。