0
0
Lập trình
TT

Hướng Dẫn Chi Tiết Về Logging HTTP Request/Response Sử Dụng Spring WebFlux

Đăng vào 1 tuần trước

• 6 phút đọc

Giới Thiệu

Chào các bạn, hôm nay mình xin giới thiệu một bài viết trong chuỗi series về Java và Spring Framework. Chúng ta sẽ cùng nhau tìm hiểu cách thức logging HTTP Request và Response bằng cách sử dụng Spring WebFlux. Rất mong những ý kiến đóng góp từ các bạn để bài viết thêm hoàn thiện.

Cấu Trúc Dự Án

Dưới đây là cấu trúc dự án mà chúng ta sẽ sử dụng trong bài hướng dẫn này:

Copy
|- spring-webflux
    |- src
        |- main
            |- java
                |- io.github.ntduycs.springwebflux
                    |- config
                    |- controller
                        |- UserController.java
                    |- filter
                        |- LoggingFilter.java
                    |- util
                    |- WebFluxApplication.java
            |- resources
    |- pom.xml

Cấu trúc này rất quen thuộc với những ai đã từng làm việc với Spring hoặc Java. Trong bài viết này, mình sẽ không giải thích sâu về cấu trúc này, tuy nhiên nếu có nhu cầu, hãy cho mình biết để mình có thể viết một bài giới thiệu chi tiết hơn.

Thêm Thư Viện Cần Thiết

Để bắt đầu với Spring WebFlux, chúng ta cần thêm một số dependencies trong file pom.xml:

xml Copy
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test-autoconfigure</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-junit-jupiter</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Với các dependencies trên, bạn chỉ cần thêm spring-boot-starter-webflux để bắt đầu. Tuy nhiên, các dependency khác sẽ hữu ích cho các bài viết sau.

Phương Pháp Thực Hiện

Chúng ta sẽ tạo một custom filter để log thông tin về request và response khi server nhận được dữ liệu từ client. Mỗi request sẽ đi qua một chuỗi các filter trước khi được xử lý bởi controller. Đây là cách mà chúng ta thực hiện logging.

Tạo Custom Filter

Spring WebFlux cung cấp khả năng tạo filter tùy chỉnh thông qua việc implement interface WebFilter:

java Copy
@Slf4j
@Component
public class LoggingFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return chain.filter(new LoggingWebExchange(exchange));
    }
}

Trong đoạn mã trên, chúng ta đã tạo một LoggingFilter để thực hiện logging cho mỗi request. Chúng ta cần thông báo cho Spring container biết về filter này bằng annotation @Component.

Tạo Logging WebExchange

Tiếp theo, để log thông tin từ request và response, chúng ta sẽ cần tạo một class LoggingWebExchange kế thừa từ ServerWebExchangeDecorator:

java Copy
static class LoggingWebExchange extends ServerWebExchangeDecorator {
    private final ServerHttpRequest request;
    private final ServerHttpResponse response;

    protected LoggingWebExchange(ServerWebExchange delegate) {
        super(delegate);
        this.request = new RequestLoggingDecorator(delegate.getRequest());
        this.response = new ResponseLoggingDecorator(delegate.getResponse());
    }

    @Override
    public ServerHttpRequest getRequest() {
        return request;
    }

    @Override
    public ServerHttpResponse getResponse() {
        return response;
    }
}

Class này cho phép ta truy cập thông tin về HTTP request và response trong quá trình xử lý.

Tạo Request Decorator

Chúng ta sẽ cần ghi lại một số thông tin quan trọng từ request:

  • HTTP method (GET, POST, ...)
  • HTTP headers
  • Request body
  • Request path
  • Request query parameters

Mã cho Request Decorator như sau:

java Copy
static class RequestLoggingDecorator extends ServerHttpRequestDecorator {
    private final Flux<DataBuffer> body;

    protected RequestLoggingDecorator(ServerHttpRequest request) {
        super(request);
        log.info("Request: {} {}", request.getMethod(), request.getPath());
        log.info("Query: {}", JsonUtils.stringify(request.getQueryParams().toSingleValueMap()));
        log.info("Headers: {}", JsonUtils.stringify(request.getHeaders().toSingleValueMap()));
        this.body = super.getBody().doOnNext(this::logBody);
    }

    @Override
    public Flux<DataBuffer> getBody() {
        return body;
    }

    private void logBody(DataBuffer dataBuffer) {
        log.debug("Request body: {}", dataBuffer.toString(StandardCharsets.UTF_8));
    }
}

Tạo Response Decorator

Tương tự như Request Decorator, ta cũng cần ghi lại thông tin từ response:

java Copy
static class ResponseLoggingDecorator extends ServerHttpResponseDecorator {
    public ResponseLoggingDecorator(ServerHttpResponse delegate) {
        super(delegate);
    }

    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        return super.writeWith(Flux.from(body).doOnNext(dataBuffer -> log.debug("Response: {}", dataBuffer.toString(StandardCharsets.UTF_8))));
    }
}

Kiểm Tra Kết Quả

Để kiểm tra xem filter đã hoạt động hay chưa, chúng ta sẽ tạo một controller đơn giản:

java Copy
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
    @GetMapping
    public Mono<ResponseEntity<ListUserResponse>> listUsers(@Valid ListUserRequest request) {
        return Mono.just(ResponseEntity.ok(new ListUserResponse()));
    }

    @PostMapping
    public Mono<ResponseEntity<Void>> createUser(@RequestBody CreateUserRequest request) {
        return Mono.just(ResponseEntity.noContent().build());
    }
}

Bây giờ, hãy chạy ứng dụng và gọi tới API để xem kết quả.

Khởi Chạy Ứng Dụng

Chúng ta có thể khởi chạy ứng dụng Spring thông qua câu lệnh sau:

Copy
mvn spring-boot:run

(Lưu ý rằng bạn cần cài đặt Maven trước khi thực hiện lệnh này)

Gọi Đến API ListUsers

Sử dụng lệnh sau để gọi đến API ListUsers:

Copy
curl 'localhost:9990/users?name=foo'

Quan sát console, bạn sẽ thấy các log đã được in ra:

Copy
INFO - Request: GET /users
INFO - Query: {"name":"foo"}
INFO - Headers: {"Host":"localhost:9990","User-Agent":"curl/8.7.1","Accept":"*/*"}

Gọi Đến API CreateUser

Sử dụng lệnh để gọi đến API CreateUser:

Copy
curl -X POST localhost:9990/users --data '{"email": "foo@bar.com", "name": "Foo Bar"}' -H 'Content-Type:application/json'

Khi kiểm tra log, bạn sẽ thấy thông tin cần thiết đã được ghi lại đầy đủ.

Kết Luận

Chúng ta vừa khám phá cách thực hiện logging cho HTTP request và response bằng Spring WebFlux. Một số điểm có thể cải thiện bao gồm:

  1. Cơ chế enable/disable logging: Tùy chỉnh logging cho các môi trường khác nhau như DEV, SIT, và PROD.
  2. Log masking/redacting: Che giấu thông tin nhạy cảm như email trong log.
  3. Định dạng log: Sử dụng JSON thay vì plain text cho log.

Cảm ơn các bạn đã theo dõi bài viết! Nếu có bất kỳ câu hỏi nào, hãy để lại comment cho mình nhé. Chúc các bạn coding vui vẻ!
source: viblo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào