milad sorour
milad sorour

Reputation: 31

How to log request URI and body along with route in Spring Cloud Gateway?

I'm working on a Spring Cloud Gateway project and I need to log the incoming request URI, body, and associated route using a GlobalFilter. Is there a recommended approach to achieve this?

I've explored various options, including implementing a custom GlobalFilter, but I'm facing difficulties in logging the route along with the request URI and body. Here's my current implementation:

import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.logging.Logger;

@Component
public class LoggingFilter extends AbstractGatewayFilterFactory<Object> {

    private static final Logger logger = Logger.getLogger(LoggingFilter.class.getName());

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();

            // Logging request URI
            logger.info("Incoming request URI: " + request.getURI());

            // Logging request body for POST and PUT requests
            if (request.getMethodValue().equalsIgnoreCase("POST") || request.getMethodValue().equalsIgnoreCase("PUT")) {
                return exchange.getRequest().getBody()
                        .flatMap(body -> {
                            String requestBody = bufferToString(body);
                            logger.info("Request body: " + requestBody);
                            return chain.filter(exchange);
                        });
            }

            return chain.filter(exchange);
        };
    }

    private String bufferToString(DataBuffer dataBuffer) {
        byte[] bytes = new byte[dataBuffer.readableByteCount()];
        dataBuffer.read(bytes);
        return new String(bytes);
    }
}

Upvotes: 0

Views: 706

Answers (2)

CostelD
CostelD

Reputation: 143

The dataBuffer can be read only once. In this case, you need to rebuild the ServerHttpRequest. The below example will work. The order of the filter should be less than -1.

 @Slf4j
public class GatewayRequestLoggingFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        if (exchange.getRequest().getHeaders().getContentLength() == 0) {
            log.info("Incoming request URI: {} with body: {}", exchange.getRequest().getURI(), "EMPTY");
            return chain.filter(exchange);
        }
        return DataBufferUtils.join(exchange.getRequest().getBody())
                .flatMap(dataBuffer -> {
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    String requestBody = new String(bytes);
                    log.info("Incoming request URI: {} with body: {}", exchange.getRequest().getURI(), requestBody);
                    ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return Flux.just(new DefaultDataBufferFactory().wrap(bytes));
                        }
                    };
                    return chain.filter(exchange.mutate().request(mutatedRequest).build());
                });
    }

    @Override
    public int getOrder() {
        return -2;
    }
}

Upvotes: 0

milad sorour
milad sorour

Reputation: 31

‍‍‍‍‍‍‍‍‍‍‍‍‍@Slf4j
public class GatewayRequestLoggingFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // Log request body for POST and PUT requests
        if (exchange.getRequest().getMethodValue().equalsIgnoreCase(HttpMethod.POST) ||
                exchange.getRequest().getMethodValue().equalsIgnoreCase(HttpMethod.PUT)) {
            return exchange.getRequest().getBody()
                    .map(dataBuffer -> {
                        byte[] bytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(bytes);
                        String requestBody = new String(bytes);
                        log.info("Incoming request URI: {} with body: {}", exchange.getRequest().getURI(), requestBody);
                        return exchange;
                    }).then(chain.filter(exchange));// Return Mono<Void>
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

Upvotes: 1

Related Questions