Spring Cloud Gateway 2.2.*记录请求和响应内容范例

836 阅读1分钟

参考: www.borischistov.com/articles/2 github.com/BorisChisto…

package demo.uyoung.example.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.dtyunxi.app.ServiceContext;
import com.dtyunxi.util.DateUtil;
import demo.uyoung.example.gateway.util.NetworkUtil;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
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.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 请求日志记录
 *
 * @date 2021.06.03
 */
@Component
public class LoggingFilter implements GlobalFilter, Ordered {


    public static final String REQID_KEY = "request_id";
    private static final Set<String> LOGGABLE_CONTENT_TYPES = new HashSet<>(Arrays.asList(
            MediaType.APPLICATION_JSON_VALUE.toLowerCase(),
            MediaType.APPLICATION_JSON_UTF8_VALUE.toLowerCase(),
            MediaType.TEXT_PLAIN_VALUE,
            MediaType.TEXT_XML_VALUE
    ));

    @Value("${support-datagram-record:false}")
    private boolean supportDatagramRecord = false;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // First we get here request in exchange
        ServerHttpRequestDecorator requestMutated = new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public Flux<DataBuffer> getBody() {
                MonoLogger requestLogger = new MonoLogger(getDelegate(), supportDatagramRecord, exchange);

                if (LOGGABLE_CONTENT_TYPES.contains(String.valueOf(getHeaders().getContentType()).toLowerCase())) {
                    return super.getBody().map(ds -> {
                        requestLogger.appendBody(ds.asByteBuffer());
                        return ds;
                    }).doFinally((s) -> requestLogger.log());
                } else {
                    requestLogger.log();
                    return super.getBody();
                }
            }
        };

        ServerHttpResponseDecorator responseMutated = new ServerHttpResponseDecorator(exchange.getResponse()) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                MonoLogger responseLogger = new MonoLogger(getDelegate());
                if (LOGGABLE_CONTENT_TYPES.contains(String.valueOf(getHeaders().getContentType()).toLowerCase())) {
                    return join(body).flatMap(db -> {
                        //不记录响应就不需要这几行
                        //responseLogger.appendBody(db.asByteBuffer());
                        //responseLogger.log();
                        return getDelegate().writeWith(Mono.just(db));
                    });
                } else {
                    responseLogger.log();
                    return getDelegate().writeWith(body);
                }
            }
        };
        return chain.filter(exchange.mutate().request(requestMutated).response(responseMutated).build());
    }


       private Mono<? extends DataBuffer> join(Publisher<? extends DataBuffer> dataBuffers) {
        Assert.notNull(dataBuffers, "'dataBuffers' must not be null");
        return Flux.from(dataBuffers)
                .collectList()
                .filter((list) -> !list.isEmpty())
                .map((list) -> list.get(0).factory().join(list))
                .doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    private static class MonoLogger {
        //这个logger用来记录当前
        private static final org.slf4j.Logger log = LoggerFactory.getLogger(LoggingFilter.class);
        //这个是用来替换 demo.uyoung.example.gateway.filter.RequestRecordFilter 的记录
        private static final org.slf4j.Logger reqLog = LoggerFactory.getLogger("demo.uyoung.example.gateway.filter.RequestRecordFilterFake");
        private StringBuilder sb = new StringBuilder();
        private CharBuffer body;
        private boolean supportDatagramRecord;
        private Map reqRecord;

        MonoLogger(ServerHttpResponse response) {

           /* sb.append("\n");
            sb.append("---- Response -----").append("\n");
            sb.append("Headers      :").append(response.getHeaders().toSingleValueMap()).append("\n");
            sb.append("Status code  :").append(response.getStatusCode()).append("\n");*/
        }

        MonoLogger(ServerHttpRequest request, boolean supportDatagramRecord, ServerWebExchange exchange) {
            /*sb.append("\n");
            sb.append("---- Request -----").append("\n");
            sb.append("Headers      :").append(request.getHeaders().toSingleValueMap()).append("\n");
            sb.append("Method       :").append(request.getMethod()).append("\n");
            sb.append("Client       :").append(request.getRemoteAddress()).append("\n");*/
            String url = request.getURI().toString();
            sb.append("开始发送请求:").append(url).append("\n");
            if (supportDatagramRecord) {
                this.supportDatagramRecord = supportDatagramRecord;
                reqRecord = saveBody(request, exchange);

            }

        }


        void appendBody(ByteBuffer byteBuffer) {
            // sb.append("Body         :").append(StandardCharsets.UTF_8.decode(byteBuffer)).append("\n");
            this.body = StandardCharsets.UTF_8.decode(byteBuffer);
        }

        
        void log() {
            /*sb.append("-------------------").append("\n");*/
            if (sb.length() > 0) {
                log.info(sb.toString());
            } else {
                log.debug("空内容");
            }
            if (supportDatagramRecord) {
                String content;
                if (reqRecord == null || reqRecord.isEmpty()) {
                    content = "{}";
                } else {
                    content = JSON.toJSONString(reqRecord, SerializerFeature.WriteMapNullValue);
                }
                reqLog.info("request record:{}", content);
            }
        }

        private HashMap<String, Object> saveBody(ServerHttpRequest request, ServerWebExchange exchange) {
            if (exchange == null) {
                log.debug("exchange 为 null,跳过");
                return null;
            }
            //exchange.getAttributes().put(REQBODY_KEY, body);
            String requestId = ServiceContext.getContext().getRequestId();
            if (StringUtils.isBlank(requestId)) {
                requestId = UUID.randomUUID().toString().replaceAll("-", "");
            }

            // 排除GET和文件上传的情况
            if (exchange.getAttributes() != null) {
                exchange.getAttributes().put(REQID_KEY, requestId);
            }
            HttpMethod method = exchange.getRequest().getMethod();
            HttpHeaders headers = exchange.getRequest().getHeaders();
            //ServerHttpRequest request = exchange.getRequest();
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();
            String uri = request.getURI().toString();
            MultiValueMap<String, String> queryParams = request.getQueryParams();

            HashMap<String, Object> reqRecord = new HashMap<>();
            // 获取真实ip
            String ip = NetworkUtil.getIpAddr(request);
            Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            reqRecord.put("routerName", route.getId());
            reqRecord.put("requestId", requestId);
            reqRecord.put("headers", headers.toSingleValueMap());
            reqRecord.put("cookies", cookies.toSingleValueMap());
            reqRecord.put("uri", uri);
            reqRecord.put("requestBody", body);
            reqRecord.put("method", method);
            reqRecord.put("remoteAddress", ip);
            reqRecord.put("queryParams", queryParams.toSingleValueMap());
            reqRecord.put("timestamp", DateUtil.getCurrentTimeNum());
            return reqRecord;
        }
    }
}