Reputation: 31
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
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
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