在 Spring 中使用日誌記錄 HTTP 請求和回應日誌
1. 概述
HTTP API 請求現在是大多數應用程式的一部分。 Logbook是一個可擴展的 Java 程式庫,可為不同的用戶端和伺服器端技術啟用完整的請求和回應日誌記錄。它允許開發人員記錄應用程式接收或發送的任何 HTTP 流量。這可用於日誌分析、稽核或調查流量問題。
在本文中,我們將介紹 Logbook 函式庫與 Spring Boot 應用程式的整合。
2. 依賴關係
要將 Logbook 庫與 Spring Boot 一起使用,我們將以下依賴項新增至專案:
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-spring-boot-starter</artifactId>
<version>3.9.0</version>
</dependency>
我們可以在 Maven Central 中找到最新版本的Logbook庫。
3. 配置
Logbook 與 Spring Boot 應用程式中的 logback 日誌記錄搭配使用。我們需要將配置加入到logback-spring.xml和application.properties檔案中。
一旦我們將 Logbook 庫新增到pom.xml中,Logbook 函式庫就會使用 Spring Boot 自動設定。讓我們為application.properties檔案新增日誌等級:
logging.level.org.zalando.logbook.Logbook=TRACE
日誌等級TRACE啟用 HTTP 請求和回應的日誌記錄。
另外,我們在logback-spring.xml檔中加入 Logbook 設定:
<logger name="org.zalando.logbook" level="INFO" additivity="false">
<appender-ref ref="RollingFile"/>
</logger>
新增後,我們可以使用 HTTP 請求運行應用程式。每次 HTTP 請求呼叫後,Logbook 庫都會將請求和回應記錄到logback-spring.xml配置中指定的日誌檔案路徑中:
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!
我們已經了解如何使用最少的配置將 Logbook 庫與 Spring Boot 整合。然而,這是基本的請求和回應日誌記錄。讓我們在下一小節中進一步了解配置。
4. 過濾和格式化
我們可以聲明 Logbook 庫配置 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;
}
在上面的程式碼範例中,我們聲明了Logbook bean,以便 Spring Boot 選擇它來載入配置。
現在,我們在建置Logbook bean 時指定了條件。從日誌記錄中排除在exclude()方法中指定的請求對映。在這種情況下,日誌庫不會記錄對應到路徑「 /api/welcome 」的 API 的請求或回應。同樣,我們使用contentType()方法對具有內容類型的請求進行過濾,並使用header()方法對具有標頭的請求進行過濾。
同樣,應包含的 HTTP API 應在include()方法中指定.如果我們不使用include()方法, Logbook 會記錄除 except exclude()方法中提到的請求之外的所有請求。
我們應該在application.properties檔案中設定過濾器屬性,以使該過濾器配置運作。屬性logbook.filter.enabled應設定為true :
logbook.filter.enabled=true
Logbook 庫使用 sl4j 記錄器記錄請求和回應,該記錄器預設使用org.zalando.logbook.Logbook類別和日誌等級trace :
sink(new DefaultSink(
new DefaultHttpLogFormatter(),
new DefaultHttpLogWriter()
))
此預設配置啟用應用程式中的日誌記錄:
GET http://localhost:8083/api/hello?name=John 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
....
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 47
Content-Type: text/html;charset=UTF-8
Date: Fri, 07 Jun 2024 11:12:27 GMT
Keep-Alive: timeout=60
Hello, John
可以將這些回應記錄到System.out或System.err中,並在控制台上列印:
Logbook logbook = Logbook.builder()
.sink(new DefaultSink(
new DefaultHttpLogFormatter(),
new StreamHttpLogWriter(System.out)
))
.build();
我們應該避免在生產環境中使用控制台列印,但如果需要,可以在開發環境中使用它。
5. 水槽
直接實作Sink介面可以實現更複雜的用例,例如,將請求/回應寫入資料庫等結構化持久性儲存。
5.1.普通水槽
Logbook 提供了一些常用日誌格式的Sink實現,即CommonsLogFormatSink和[ExtendedLogFormatSink](https://en.wikipedia.org/wiki/Extended_Log_Format) 。
ChunkingSink將長訊息分割成較小的區塊並單獨寫入它們,同時將它們委託給另一個接收器:
Logbook logbook = Logbook.builder()
.sink(new ChunkingSink(sink, 1000))
.build();
5.2. Logstash 接收器
Logbook 在附加庫中提供了logstash編碼器。讓我們來看一個LogstashLogbackSink的範例。為此,我們在pom.xml中加入[logstash](https://mvnrepository.com/artifact/org.zalando/logbook-logstash)和[logstash-encoder](https://mvnrepository.com/artifact/net.logstash.logback/logstash-logback-encoder)依賴項:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-logstash</artifactId>
<version>3.9.0</version>
</dependency>
然後,我們更改logback-spring.xml中appender下的編碼器。日誌儲存編碼器啟用LogstashLogbackSink :
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
...
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
現在我們聲明LogstashLogbackSink並將其加入到Logbook物件建構器中:
HttpLogFormatter formatter = new JsonHttpLogFormatter();
LogstashLogbackSink logstashsink = new LogstashLogbackSink(formatter);
Logbook logbook = Logbook.builder()
.sink(logstashsink)
.build();
這裡我們使用了JsonHttpLogFormatter和LogstashLogbackSink,此自訂以 JSON 格式列印日誌:
{
"@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!"
}
}
JSON日誌單行列印;為了更好的可讀性,我們在這裡對其進行了格式化。
我們可以在聲明LogstashLogbackSink物件時更改日誌等級:
LogstashLogbackSink logstashsink = new LogstashLogbackSink(formatter, Level.INFO);
我們也可以將SplunkHttpLogFormatter與日誌接收器一起使用。它以鍵值格式列印日誌:
origin=remote ... method=GET uri=http://localhost:8083/api/hello?name=John host=localhost path=/api/hello ...
5.3.複合水槽
組合多個接收器我們可以形成一個CompositeSink :
CompositeSink compsink = new CompositeSink(Arrays.asList(logstashsink, new CommonsLogFormatSink(new DefaultHttpLogWriter())));
Logbook logbook = Logbook.builder()
.sink(compsink)
.build();
此配置使用複合接收器中指定的所有接收器記錄請求詳細資訊。在這裡,Logbook 透過組合兩個接收器來記錄請求:
... "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"],...
六,結論
在本文中,我們學習如何使用最少的配置將 Logbook 庫與 Spring Boot 整合。另外,關於使用exclude()和include()方法過濾請求路徑。我們也了解如何使用LogstashLogbackSink等 Sink 實作以及JsonHttpLogFormatter等格式化程式根據需求自訂日誌格式。
與往常一樣,原始碼範例可以在 GitHub 上取得。