伺服器上的 Jersey 日誌記錄指南
1. 簡介
在本教程中,我們將探討如何在 Jersey 伺服器應用程式中啟用、設定和自訂日誌記錄,並示範如何使用整合測試驗證日誌記錄行為。
2. 場景設定
讓我們從最基本的 Jersey 伺服器設定開始。我們將建立一個簡單的 REST 端點,並配置 Jersey 來記錄傳入的請求和傳出的回應。我們將使用 SLF4J 作為日誌記錄後端,這是 Java 專案中的常見選擇。
2.1. 建立端點
我們的資源包含一個傳回String的端點,但可以傳回任何其他類型:
@Path("/logging")
public class LoggingResource {
@GET
public String get() {
return "Hello";
}
}
2.2. 註冊資源
我們將在 Jersey 應用程式配置類別中註冊此資源:
public class JerseyServerLoggingApp extends ResourceConfig {
public JerseyServerLoggingApp() {
register(LoggingResource.class);
// ...
}
}
3. 在 Jersey 中啟用日誌記錄
Jersey 透過LoggingFeature提供了一個內建的日誌記錄功能,可以在應用程式中註冊。此功能會記錄不同等級的 HTTP 流量(例如 headers、payload 等),我們可以透過LoggingFeature.Verbosity枚舉進行選擇。
我們將透過傳遞Logger 、日誌等級、詳細程度和最大實體大小來註冊它。合適的最大實體大小取決於我們的應用程式需求。如果我們要記錄有效負載,並且其中包含大量資料(例如 base-64 屬性),則限制實體大小可以避免最終產生大量無法讀取的日誌:
register(new LoggingFeature(
Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME),
Level.INFO,
LoggingFeature.Verbosity.PAYLOAD_ANY,
8192)
);
此配置記錄所有請求和回應有效負載,最大可達 8192 位元組(8 kiB)。
4. 實作自訂日誌記錄
我們可以從**JAX-RS API**實作自訂的**ContainerRequestFilter**或**ContainerResponseFilter**以獲得更好的控制。這使我們能夠記錄特定的詳細資訊或根據需要格式化日誌。每個介面都定義了一個filter方法,該方法會公開一個包含請求或回應資訊的上下文物件。
4.1. 實作ContainerRequestFilter和ContainerResponseFilter
讓我們建立一個實作ContainerRequestFilter和ContainerResponseFilter的自訂日誌過濾器:
@Provider
public class CustomServerLoggingFilter
implements ContainerRequestFilter, ContainerResponseFilter {
static final Logger LOG = LoggerFactory.getLogger(CustomServerLoggingFilter.class);
// ...
}
這段程式碼設定了一個同時實作請求和回應過濾器的提供者類,並初始化了一個用於這兩種方法的記錄器。接下來,我們實作請求日誌記錄方法:
@Override
public void filter(ContainerRequestContext requestContext) {
LOG.info(
"Incoming request: {} {}",
requestContext.getMethod(),
requestContext.getUriInfo().getRequestUri());
}
每個傳入的 HTTP 請求都會呼叫此方法。它會記錄 HTTP 方法和請求 URI,這有助於追蹤哪些端點正在被存取。類似地,我們實作了響應日誌記錄方法:
@Override
public void filter(
ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
LOG.info(
"Outgoing response: {} {} - Status {}",
requestContext.getMethod(),
requestContext.getUriInfo().getRequestUri(),
responseContext.getStatus());
}
每次發出 HTTP 回應時都會呼叫此方法。它會記錄 HTTP 方法、請求 URI 和回應狀態碼,從而提供請求/回應週期的完整資訊。
4.2. 註冊自訂過濾器
最後,讓我們在應用程式設定中註冊這個過濾器:
register(CustomServerLoggingFilter.class);
5. 建立日誌記錄整合測試
我們可以編寫一個整合測試來驗證日誌記錄,該測試啟動 Jersey 伺服器,發出請求並檢查日誌。讓我們從伺服器設定開始:
class JerseyLoggingIntegrationTest {
private static HttpServer server;
private static final URI BASE_URI = URI.create("http://localhost:8080/api");
@BeforeAll
static void setup() throws IOException {
server = GrizzlyHttpServerFactory.createHttpServer(
BASE_URI, new JerseyLoggingServerApp());
}
@AfterAll
static void teardown() {
server.shutdownNow();
}
// ...
}
這將設定整合測試類,定義伺服器和基本 URI,並提供 setup 和 teardown 方法,用於在所有測試之前和之後啟動和停止 Jersey 伺服器。現在,讓我們新增一個測試,註冊一個記錄器,用於斷言配置是否有效,並由我們的自訂日誌過濾器呼叫。我們將使用ListAppender來實現這一點:
@Test
void whenRequestMadeWithLoggingFilter_thenCustomLogsAreWritten() {
Logger logger = (Logger) LoggerFactory.getLogger(CustomServerLoggingFilter.class);
ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
listAppender.start();
logger.addAppender(listAppender);
listAppender.list.clear();
Response response = ClientBuilder.newClient()
.target(BASE_URI + "/logging")
.request()
.get();
assertEquals(200, response.getStatus());
// ...
}
這裡,我們配置了一個ListAppender來從CustomServerLoggingFilter記錄器擷取日誌,啟動它並清除所有先前的日誌。然後,我們向/logging端點發出 GET 請求,並斷言回應狀態為 200。
最終,我們可以遍歷我們的日誌並檢查由我們的過濾器創建的日誌是否存在:
boolean requestLogFound = listAppender.list.stream().anyMatch(
event -> event.getFormattedMessage().contains(
"Incoming request: GET http://localhost:8080/api/logging"));
boolean responseLogFound = listAppender.list.stream().anyMatch(
event -> event.getFormattedMessage().contains(
"Outgoing response: GET http://localhost:8080/api/logging - Status 200"));
assertEquals(true, requestLogFound);
assertEquals(true, responseLogFound);
logger.detachAppender(listAppender);
我們檢查捕獲的日誌中是否存在傳入請求和傳出回應的預期日誌訊息。然後,我們分離附加程式以在測試結束後進行清理。
6. 結論
在本文中,我們了解了 Jersey 如何輕鬆啟用和自訂伺服器端日誌記錄。內建的Logging eature足以滿足大多數用例的需求,但自訂過濾器可以為進階場景提供完全的控制。
與往常一樣,原始碼可在 GitHub 上取得。