Spring-cloud-gateway: Log Request/Response headers/body

Created on 9 Apr 2019  路  6Comments  路  Source: spring-cloud/spring-cloud-gateway

Question

Hi guys I want to create a simple filter to log request and responses, their headers and body for all requests(GET/POST/PUT/DELETE) passing through the gateway.
Is it possible to do this?
I cannot find a good solution to read the request/response body

question

Most helpful comment

Hi!

I need to get the request body content for routes defined in the application.yml
If I use a custom RouteLocator the

exchange.getAttribute("cachedRequestBodyObject")

is filled for all requests (also the one out of the yml)

you can find the code here

https://gist.github.com/matzegebbe/bf631b2d3ab6d55f58f4b6c1d3511189

curl -H "x-debug: /nginx/" -X POST -d "{'data':'fooBar'}" http://localhost:8080/nginx/ 

with application.yml

- id: nginx
  uri: http://localhost:8082
  predicates:
    - Path=/nginx/**
  filters:
    - StripPrefix=1

gives me the log

n.h.apigateway.ApiGatewayApplication     : Started ApiGatewayApplication in 3.666 seconds (JVM running for 4.311)
n.h.a.filter.RequestResponseLogFilter    : Request Scheme:http,Path:/nginx/
n.h.a.filter.RequestResponseLogFilter    : Request Method:POST,IP:/0:0:0:0:0:0:0:1%0:48774,Host:localhost
n.h.a.filter.RequestResponseLogFilter    : Request ContentType:application/x-www-form-urlencoded,Content Length:17
n.h.a.filter.RequestResponseLogFilter    : Request Body:{'data':'fooBar'}
n.h.a.filter.RequestResponseLogFilter    : Response HttpStatus:200 OK
n.h.a.filter.RequestResponseLogFilter    : Response ContentType:null,Content Length:-1
n.h.a.filter.RequestResponseLogFilter    : Response Original Path:/nginx/,Cost:15 ms
n.h.a.filter.RequestResponseLogFilter    : Response:<html>
<head><title>405 Not Allowed</title></head>
<body>
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.17.2</center>
</body>
</html>

All 6 comments

Please note that I have a working function for this:

@Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String requestMethod = exchange.getRequest().getMethod().toString();
        String path = exchange.getRequest().getPath().toString();
        String headers = exchange.getRequest().getHeaders().toSingleValueMap().toString();
        //TODO: propper read request body when available
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            String responseHeaders = exchange.getRequest().getHeaders().toSingleValueMap().toString();
            int status = exchange.getResponse().getStatusCode().value();
            //TODO: propper read response body when available
        }));
    }

What you have looks fine for headers (though you'll loose multi value headers).

For the request/response body I wouldn't recommend doing it 100% of the time since you'll have to buffer all requests/responses into memory. See the Modify filters here https://github.com/spring-cloud/spring-cloud-gateway/tree/master/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/factory/rewrite

I custom a plugin project for spring-cloud-gateway.

You can take a look at it . Issues and PR welcome.

https://github.com/chenggangpro/spring-cloud-gateway-plugin

BTW: Read Request And Response cost a lot of memory.Wouldn't recommend doing it 100% of the time.

@chenggangpro I will take a look at it in the weekend :) thanks
@spencergibb thanks for the feedback.
We can close this topic

Hi!

I need to get the request body content for routes defined in the application.yml
If I use a custom RouteLocator the

exchange.getAttribute("cachedRequestBodyObject")

is filled for all requests (also the one out of the yml)

you can find the code here

https://gist.github.com/matzegebbe/bf631b2d3ab6d55f58f4b6c1d3511189

curl -H "x-debug: /nginx/" -X POST -d "{'data':'fooBar'}" http://localhost:8080/nginx/ 

with application.yml

- id: nginx
  uri: http://localhost:8082
  predicates:
    - Path=/nginx/**
  filters:
    - StripPrefix=1

gives me the log

n.h.apigateway.ApiGatewayApplication     : Started ApiGatewayApplication in 3.666 seconds (JVM running for 4.311)
n.h.a.filter.RequestResponseLogFilter    : Request Scheme:http,Path:/nginx/
n.h.a.filter.RequestResponseLogFilter    : Request Method:POST,IP:/0:0:0:0:0:0:0:1%0:48774,Host:localhost
n.h.a.filter.RequestResponseLogFilter    : Request ContentType:application/x-www-form-urlencoded,Content Length:17
n.h.a.filter.RequestResponseLogFilter    : Request Body:{'data':'fooBar'}
n.h.a.filter.RequestResponseLogFilter    : Response HttpStatus:200 OK
n.h.a.filter.RequestResponseLogFilter    : Response ContentType:null,Content Length:-1
n.h.a.filter.RequestResponseLogFilter    : Response Original Path:/nginx/,Cost:15 ms
n.h.a.filter.RequestResponseLogFilter    : Response:<html>
<head><title>405 Not Allowed</title></head>
<body>
<center><h1>405 Not Allowed</h1></center>
<hr><center>nginx/1.17.2</center>
</body>
</html>

Here is a variant that use modify filters to capture and log request and response body

@Bean
    public RouteLocator myRouteSavingRequestBody(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("my-route-id",
                p -> p
                    .path("/v2/**") //your own path filter
                    .filters(f -> f
                        .modifyResponseBody(String.class, String.class,
                            (webExchange, originalBody) -> {
                                if (originalBody != null) {
                                    logger.debug("Response body {}", originalBody);
                                    return Mono.just(originalBody);
                                } else {
                                    return Mono.empty();
                                }
                            })
                        .modifyRequestBody(String.class, String.class,
                            (webExchange, originalBody) -> {
                                if (originalBody != null) {
                                    logger.debug("Request body {}", originalBody);
                                    return Mono.just(originalBody);
                                } else {
                                    return Mono.empty();
                                }
                            })

                    )
                    .uri("https://myuri.org")
            )
            .build();
    }
Was this page helpful?
0 / 5 - 0 ratings