-
-
Save MdShohanurRahman/0d27148e7a1c9e05f6542f11c2ad98a0 to your computer and use it in GitHub Desktop.
Spring Cloud Gateway Custom Logging
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.fasterxml.jackson.databind.ObjectMapper; | |
import lombok.RequiredArgsConstructor; | |
import lombok.extern.slf4j.Slf4j; | |
import org.reactivestreams.Publisher; | |
import org.springframework.cloud.gateway.filter.GatewayFilterChain; | |
import org.springframework.cloud.gateway.filter.GlobalFilter; | |
import org.springframework.core.Ordered; | |
import org.springframework.core.io.buffer.DataBuffer; | |
import org.springframework.core.io.buffer.DataBufferFactory; | |
import org.springframework.core.io.buffer.DataBufferUtils; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.MediaType; | |
import org.springframework.http.server.reactive.ServerHttpRequest; | |
import org.springframework.http.server.reactive.ServerHttpRequestDecorator; | |
import org.springframework.http.server.reactive.ServerHttpResponse; | |
import org.springframework.http.server.reactive.ServerHttpResponseDecorator; | |
import org.springframework.stereotype.Component; | |
import org.springframework.web.server.ServerWebExchange; | |
import reactor.core.publisher.Flux; | |
import reactor.core.publisher.Mono; | |
import java.nio.charset.StandardCharsets; | |
import java.util.stream.Collectors; | |
@Slf4j | |
@RequiredArgsConstructor | |
@Component | |
public class LoggingFilter implements GlobalFilter, Ordered { | |
private final ObjectMapper objectMapper; | |
@Override | |
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { | |
ServerHttpRequest request = exchange.getRequest(); | |
ServerHttpResponse originalResponse = exchange.getResponse(); | |
DataBufferFactory bufferFactory = originalResponse.bufferFactory(); | |
return DataBufferUtils.join(request.getBody()) | |
.defaultIfEmpty(bufferFactory.wrap(new byte[0])) | |
.flatMap(dataBuffer -> { | |
byte[] bytes = new byte[dataBuffer.readableByteCount()]; | |
dataBuffer.read(bytes); | |
DataBufferUtils.release(dataBuffer); | |
// Log the request details | |
logRequestDetails(request, bytes); | |
// Mutate the request to include the body | |
ServerHttpRequest mutatedRequest = request.mutate().build(); | |
DataBuffer bodyDataBuffer = bufferFactory.wrap(bytes); | |
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer); | |
mutatedRequest = new ServerHttpRequestDecorator(mutatedRequest) { | |
@Override | |
public Flux<DataBuffer> getBody() { | |
return bodyFlux; | |
} | |
}; | |
// Decorate the response | |
ServerHttpResponseDecorator decoratedResponse = getResponseDecorator(exchange); | |
return chain.filter(exchange.mutate().request(mutatedRequest).response(decoratedResponse).build()); | |
}); | |
} | |
private void logRequestDetails(ServerHttpRequest request, byte[] requestBodyBytes) { | |
String requestUri = request.getURI().toString(); | |
String method = request.getMethod().name(); | |
HttpHeaders headers = request.getHeaders(); | |
String headersString = headers.entrySet().stream() | |
.map(entry -> entry.getKey() + ": " + String.join(", ", entry.getValue())) | |
.collect(Collectors.joining("\n")); | |
log.info("Request URI: {}", requestUri); | |
log.info("Request Method: {}", method); | |
log.info("Request Headers: \n{}", headersString); | |
log.info("Request QueryParams: \n{}", request.getQueryParams()); | |
String requestBody = new String(requestBodyBytes, StandardCharsets.UTF_8); | |
if (headers.getContentType() != null && headers.getContentType().includes(MediaType.APPLICATION_JSON)) { | |
try { | |
Object json = objectMapper.readValue(requestBody, Object.class); | |
requestBody = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json); | |
} catch (Exception e) { | |
log.error("Failed to pretty print JSON request body", e); | |
} | |
} | |
log.info("Request Body: \n{}", requestBody); | |
} | |
private ServerHttpResponseDecorator getResponseDecorator(ServerWebExchange exchange) { | |
ServerHttpResponse originalResponse = exchange.getResponse(); | |
DataBufferFactory bufferFactory = originalResponse.bufferFactory(); | |
return new ServerHttpResponseDecorator(originalResponse) { | |
@Override | |
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { | |
if (body instanceof Flux) { | |
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body; | |
return super.writeWith(fluxBody.map(dataBuffer -> { | |
byte[] responseBytes = new byte[dataBuffer.readableByteCount()]; | |
dataBuffer.read(responseBytes); | |
DataBufferUtils.release(dataBuffer); | |
String responseBody = new String(responseBytes, StandardCharsets.UTF_8); | |
// Pretty print JSON responses | |
if (originalResponse.getHeaders().getContentType() != null && | |
originalResponse.getHeaders().getContentType().includes(MediaType.APPLICATION_JSON)) { | |
try { | |
Object json = objectMapper.readValue(responseBody, Object.class); | |
responseBody = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json); | |
} catch (Exception e) { | |
log.error("Failed to pretty print JSON response", e); | |
} | |
} | |
log.info("Response Code: {}", exchange.getResponse().getStatusCode()); | |
log.info("Response Body: \n{}", responseBody); | |
return bufferFactory.wrap(responseBytes); | |
})); | |
} | |
return super.writeWith(body); | |
} | |
}; | |
} | |
@Override | |
public int getOrder() { | |
return -2; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment