精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

SpringCloud 微服務中網關如何記錄請求響應日志?

網絡 網絡管理
getOrder()方法返回的值必須要<-1,否則標準的NettyWriteResponseFilter將在您的過濾器被調用的機會之前發送響應,即不會執行獲取后端響應參數的方法。

大家好,我是飄渺。

在基于SpringCloud開發的微服務中,我們一般會選擇在網關層記錄請求和響應日志,并將其收集到ELK中用作查詢和分析。

今天我們就來看看如何實現此功能。

日志實體類

首先我們在網關中定義一個日志實體,用于組裝日志對象;

@Data
public class AccessLog {

    /**用戶編號**/
    private Long userId;

    /**路由**/
    private String targetServer;

    /**協議**/
    private String schema;
    
    /**請求方法名**/
    private String requestMethod;
    
    /**訪問地址**/
    private String requestUrl;

    /**請求IP**/
    private String clientIp;

    /**查詢參數**/
    private MultiValueMap<String, String> queryParams;
    
    /**請求體**/
    private String requestBody;
    
    /**請求頭**/
    private MultiValueMap<String, String> requestHeaders;

     /**響應體**/
    private String responseBody;
    
    /**響應頭**/
    private MultiValueMap<String, String> responseHeaders;
    
     /**響應結果**/
    private HttpStatusCode httpStatusCode;
    
     /**開始請求時間**/
    private LocalDateTime startTime;
    
    /**結束請求時間**/
    private LocalDateTime endTime;
    
    /**執行時長,單位:毫秒**/
    private Integer duration;

}

網關日志過濾器

接下來我們在網關中定義一個Filter,用于收集日志信息。

@Component
public class AccessLogFilter implements GlobalFilter, Ordered {

    private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();

    /**
     * 打印日志
     * @param accessLog 網關日志
     */
    private void writeAccessLog(AccessLog accessLog) {
        log.info("----access---- : {}", JsonUtils.obj2StringPretty(accessLog));
    }

    /**
     * 順序必須是<-1,否則標準的NettyWriteResponseFilter將在您的過濾器得到一個被調用的機會之前發送響應
     * 也就是說如果不小于 -1 ,將不會執行獲取后端響應的邏輯
     * @return
     */
    @Override
    public int getOrder() {
        return -100;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 將 Request 中可以直接獲取到的參數,設置到網關日志
        ServerHttpRequest request = exchange.getRequest();

        AccessLog gatewayLog = new AccessLog();
        gatewayLog.setTargetServer(WebUtils.getGatewayRoute(exchange).getId());
        gatewayLog.setSchema(request.getURI().getScheme());
        gatewayLog.setRequestMethod(request.getMethod().name());
        gatewayLog.setRequestUrl(request.getURI().getRawPath());
        gatewayLog.setQueryParams(request.getQueryParams());
        gatewayLog.setRequestHeaders(request.getHeaders());
        gatewayLog.setStartTime(LocalDateTime.now());
        gatewayLog.setClientIp(WebUtils.getClientIP(exchange));

        // 繼續 filter 過濾
        MediaType mediaType = request.getHeaders().getContentType();
        if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)
                || MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) { // 適合 JSON 和 Form 提交的請求
            return filterWithRequestBody(exchange, chain, gatewayLog);
        }
        return filterWithoutRequestBody(exchange, chain, gatewayLog);
    }


    /**
     * 沒有請求體的請求只需要記錄日志
     */
    private Mono<Void> filterWithoutRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog accessLog) {
        // 包裝 Response,用于記錄 Response Body
        ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);

        return chain.filter(exchange.mutate().response(decoratedResponse).build())
                .then(Mono.fromRunnable(() -> writeAccessLog(accessLog)));
    }

    /**
     * 需要讀取請求體
     * 參考 {@link ModifyRequestBodyGatewayFilterFactory} 實現
     */
    private Mono<Void> filterWithRequestBody(ServerWebExchange exchange, GatewayFilterChain chain, AccessLog gatewayLog) {
        // 設置 Request Body 讀取時,設置到網關日志
        ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);

        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
            gatewayLog.setRequestBody(body);
            return Mono.just(body);
        });

        // 通過 BodyInserter 插入 body(支持修改body), 避免 request body 只能獲取一次
        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
        HttpHeaders headers = new HttpHeaders();
        headers.putAll(exchange.getRequest().getHeaders());
        // the new content type will be computed by bodyInserter
        // and then set in the request decorator
        headers.remove(HttpHeaders.CONTENT_LENGTH);

        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);

        // 通過 BodyInserter 將 Request Body 寫入到 CachedBodyOutputMessage 中
        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
            // 重新封裝請求
            ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);
            // 記錄響應日志
            ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog);
            // 記錄普通的
            return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build())
                    .then(Mono.fromRunnable(() -> writeAccessLog(gatewayLog))); // 打印日志

        }));
    }

    /**
     * 記錄響應日志
     * 通過 DataBufferFactory 解決響應體分段傳輸問題。
     */
    private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, AccessLog accessLog) {
        ServerHttpResponse response = exchange.getResponse();

        return new ServerHttpResponseDecorator(response) {

            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    // 計算執行時間
                    accessLog.setEndTime(LocalDateTime.now());
                    accessLog.setDuration((int) (LocalDateTimeUtil.between(accessLog.getStartTime(),
                            accessLog.getEndTime()).toMillis()));
                    accessLog.setResponseHeaders(response.getHeaders());
                    accessLog.setHttpStatusCode(response.getStatusCode());

                    // 獲取響應類型,如果是 json 就打印
                    String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);

                    if (StrUtil.isNotBlank(originalResponseContentType)
                            && originalResponseContentType.contains("application/json")) {
                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);

                        return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                            // 設置 response body 到網關日志
                            byte[] content = readContent(dataBuffers);
                            String responseResult = new String(content, StandardCharsets.UTF_8);
                            accessLog.setResponseBody(responseResult);

                            // 響應
                            return bufferFactory.wrap(content);
                        }));
                    }
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
    }


    /**
     * 請求裝飾器,支持重新計算 headers、body 緩存
     *
     * @param exchange 請求
     * @param headers 請求頭
     * @param outputMessage body 緩存
     * @return 請求裝飾器
     */
    private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
        return new ServerHttpRequestDecorator(exchange.getRequest()) {

            @Override
            public HttpHeaders getHeaders() {
                long contentLength = headers.getContentLength();
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                if (contentLength > 0) {
                    httpHeaders.setContentLength(contentLength);
                } else {
                    // TODO: this causes a 'HTTP/1.1 411 Length Required' // on
                    // httpbin.org
                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                }
                return httpHeaders;
            }

            @Override
            public Flux<DataBuffer> getBody() {
                return outputMessage.getBody();
            }
        };
    }

    /**
     * 從dataBuffers中讀取數據
     * @author jam
     * @date 2024/5/26 22:31
     */
    private byte[] readContent(List<? extends DataBuffer> dataBuffers) {
        // 合并多個流集合,解決返回體分段傳輸
        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
        DataBuffer join = dataBufferFactory.join(dataBuffers);
        byte[] content = new byte[join.readableByteCount()];
        join.read(content);
        // 釋放掉內存
        DataBufferUtils.release(join);
        return content;
    }

}

代碼較長建議直接拷貝到編輯器,只要注意下面一個關鍵點:

getOrder()方法返回的值必須要<-1,否則標準的NettyWriteResponseFilter將在您的過濾器被調用的機會之前發送響應,即不會執行獲取后端響應參數的方法。

通過上面的兩步我們已經可以獲取到請求的輸入輸出參數了,在 writeAccessLog()中將其打印到日志文件,方便通過ELK進行收集。

在實際項目中,網關日志量一般會非常大,不建議使用數據庫進行存儲。

實際效果

服務正常響應:

圖片

服務異常響應:

圖片圖片

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2021-03-26 06:01:45

日志MongoDB存儲

2021-03-09 09:33:42

網關授權微服務

2021-08-11 05:00:48

Spring 日志手段

2021-08-13 07:52:35

微服務網關數據

2022-05-12 08:21:13

項目網關模塊

2021-05-14 09:15:32

SpringCloud微服務日志

2021-06-09 09:42:50

SpringCloud微服務灰度發布

2024-03-18 08:48:52

Spring多端認證微服務

2022-04-14 08:51:49

微服務Redisson分布式鎖

2025-06-17 07:37:53

2011-08-01 13:57:20

iPhone 網絡

2024-03-06 08:36:36

2022-09-01 08:17:15

Gateway微服務網關

2017-03-09 19:39:54

微服務架構重構

2023-06-09 14:46:36

2019-09-24 08:44:09

OpenrestyAPI網關

2022-05-16 08:22:11

網關過濾器路由

2021-01-25 15:00:44

微服務分布式日志

2024-08-05 10:03:53

2024-10-29 08:44:18

點贊
收藏

51CTO技術棧公眾號

91久久中文| 唐人社导航福利精品| 成人午夜av影视| 日本精品视频在线| 日韩精品123区| 大桥未久女教师av一区二区| 欧美丝袜一区二区三区| 中文字幕一区二区中文字幕| 四虎永久在线观看| 久久精品国产精品亚洲综合| 国模私拍一区二区三区| 欧美福利在线视频| 亚洲涩涩av| 日韩午夜精品视频| 一区二区三区网址| 超碰97免费在线| 国产精品素人视频| 欧美乱偷一区二区三区在线| 午夜久久久久久噜噜噜噜| 日韩精品一区第一页| 国外视频精品毛片| 91 在线视频| 禁果av一区二区三区| 精品国产乱码久久久久久免费| 国产免费又粗又猛又爽| 欧美aa在线| 亚洲无人区一区| 日韩人妻精品一区二区三区| 77777影视视频在线观看| av一二三不卡影片| 国产chinese精品一区二区| 91精品视频免费在线观看 | 99久久99久久精品| 日韩精品成人av| 国产蜜臀97一区二区三区| 久久精品国产理论片免费| 国内精品久久久久久久久久久| 青青草国产成人99久久| 欧美中文在线免费| 久久久久久久99| 国产精品a级| 欧美床上激情在线观看| 夫妻性生活毛片| 91亚洲国产高清| 自拍视频国产精品| 日韩不卡av在线| 国产精品一线天粉嫩av| 亚洲精品综合久久中文字幕| 特级西西人体4444xxxx| 天天久久夜夜| 亚洲男人av在线| 丰满圆润老女人hd| 欧州一区二区| 在线不卡国产精品| 国产美女网站视频| 91亚洲国产成人久久精品| 久久久国产一区二区| 特黄一区二区三区| 香蕉综合视频| 欧美激情视频一区二区三区不卡| 国产高潮国产高潮久久久91| 欧美激情1区2区| 久久久久免费精品国产| 亚洲一区欧美在线| 久久精品1区| 国产精品国产福利国产秒拍| 亚洲天堂网视频| 黑人精品欧美一区二区蜜桃 | 涩涩视频在线观看| 久久99国产精品成人| 亚洲va男人天堂| 国模无码一区二区三区| 97成人超碰视| 午夜欧美性电影| 欧美激情免费| 亚洲一区二区三区在线播放| 阿v天堂2017| 天然素人一区二区视频| 欧美另类久久久品| 精品无码人妻少妇久久久久久| 欧美日韩一区二区三区在线电影 | 欧美在线小视频| 成人亚洲免费视频| 国产精品1luya在线播放| 亚洲欧洲一区二区三区久久| 国产在视频线精品视频| 韩日精品在线| 国产成人一区二区三区小说| 国产一区二区女内射| 成人黄色网址在线观看| 日本一区二区视频| 特级毛片在线| 91久久久免费一区二区| 午夜大片在线观看| 亚洲影院天堂中文av色| 久久夜色精品国产| 国产一级淫片a视频免费观看| 韩国v欧美v日本v亚洲v| 久久久久久久久久码影片| 91精彩视频在线观看| 亚洲永久免费av| 丝袜制服一区二区三区| 中文字幕久久精品一区二区| 亚洲人在线观看| 久久久久97国产| 日本欧美韩国一区三区| 国产经典一区二区三区| 秋霞成人影院| 欧美视频免费在线| 韩国三级hd中文字幕有哪些| 禁断一区二区三区在线| 欧美高清性xxxxhd| 特级西西444www大胆免费看| 国产999精品久久久久久绿帽| 欧美一区视久久| h片在线观看| 欧美久久一区二区| 国产精品20p| 一本色道88久久加勒比精品| 91久久综合亚洲鲁鲁五月天| 国产在线视频福利| 久久精品欧美一区二区三区不卡| 国产一区二区片| 国产资源一区| 亚洲人免费视频| 亚洲国产成人精品激情在线| 国产传媒久久文化传媒| 在线观看福利一区| 国产欧美一区二区三区不卡高清| 99视频精品| 狠狠躁18三区二区一区| 亚洲一二三av| 日韩久久电影| 久久69精品久久久久久久电影好| 三级网站在线播放| 91一区二区三区在线观看| 免费在线精品视频| 激情久久一区二区| 宅男66日本亚洲欧美视频| 极品国产91在线网站| 97久久久精品综合88久久| 亚洲熟妇无码一区二区三区导航| 色999久久久精品人人澡69| 在线观看欧美成人| 艳妇乳肉豪妇荡乳av无码福利| 久久久电影一区二区三区| 丰满少妇被猛烈进入高清播放| 精品肉丝脚一区二区三区| 亚洲青涩在线| 国模精品娜娜一二三区| 超级白嫩亚洲国产第一| 亚洲丁香久久久| 国产精品99精品无码视| 大尺度一区二区| av在线播放亚洲| 欧美男人操女人视频| 国产69精品久久久| 亚洲av成人无码久久精品老人| 亚洲www啪成人一区二区麻豆| 国产午夜在线一区二区三区| 亚洲国产欧美国产综合一区| 久久久久久一区| 中国色在线日|韩| 国产午夜精品美女视频明星a级| 中文字幕精品视频在线观看| 国产欧美日韩在线| 中文字幕12页| 黄色在线一区| 欧美日韩最好看的视频| 色成人免费网站| 欧美成人免费观看| 日韩性xxxx| 在线观看区一区二| 人人澡人人澡人人看| 国产成人小视频| 无码 制服 丝袜 国产 另类| 香蕉久久夜色精品国产更新时间| 日本欧美爱爱爱| 国产视频网址在线| 欧美精品一级二级三级| 国产亚洲精品女人久久久久久| a级精品国产片在线观看| 农村妇女精品一二区| 国产精品91一区二区三区| 99久久自偷自偷国产精品不卡| 欧美亚洲日本精品| 国产精品―色哟哟| 午夜视频在线网站| 亚洲激情亚洲| 亚洲精品中文字幕乱码三区不卡 | 亚洲影视在线观看| 97超碰在线资源| 国产精品18久久久久久久网站| 国产美女主播在线播放| 日韩一区二区三区免费播放| 国产日韩久久| 久久精品嫩草影院| 91精品国产成人| 黄网页在线观看| 日韩成人在线免费观看| 国产精品女同一区二区| 欧美性猛交xxxx乱大交蜜桃| 在线观看成人毛片| 亚洲国产精品高清| 国产乱了高清露脸对白| 国产在线麻豆精品观看| 任你操这里只有精品| 激情综合电影网| 正在播放亚洲| 国产中文精品久高清在线不| 国产欧美日韩在线播放| 亚洲午夜国产成人| 欧美一级淫片aaaaaaa视频| 怡红院在线观看| 最近2019年手机中文字幕| 五月天激情婷婷| 精品奇米国产一区二区三区| 亚洲中文字幕在线观看| 一本大道久久精品懂色aⅴ | 伊人精品成人久久综合软件| 在线观看一区二区三区三州| 国产精品嫩模av在线| 精品国产91亚洲一区二区三区www| 香蕉久久久久久| 国产精品久久不能| 桃花岛tv亚洲品质| 日本成人激情视频| 亚洲人成在线网站| 91tv亚洲精品香蕉国产一区7ujn| 欧美1234区| 欧美黑人xxx| 七七久久电影网| 欧美成人激情视频免费观看| 国内精品久久久久久野外| 在线视频免费一区二区| 国产爆初菊在线观看免费视频网站| 日韩电影大全免费观看2023年上| 亚洲AV无码一区二区三区少妇| 欧美电影影音先锋| 国产巨乳在线观看| 在线不卡中文字幕| 国产人妻精品一区二区三区| 91精品国产一区二区三区蜜臀 | 欧美精品一区二区三| 亚洲国产精品久久久久爰性色| 91精品国产综合久久精品麻豆| 亚洲熟妇av乱码在线观看| 欧美色手机在线观看| 中文字幕一区二区三区四区视频 | 136fldh精品导航福利| 福利影院在线看| 91国产中文字幕| 都市激情综合| 国产精品久久久久久久久男 | 国产在视频线精品视频| 亚洲欧美在线视频| 欧美又粗又大又长| 亚洲一区二区欧美日韩| 日韩手机在线观看| 色婷婷综合久久久久中文一区二区| 无码人妻丰满熟妇精品区| 色国产综合视频| 亚洲永久精品视频| 精品美女一区二区| 色网站在线免费观看| 在线日韩精品视频| wwwav在线| 97视频在线播放| 高清av一区二区三区| 91久久中文字幕| 精品久久97| 日韩欧美亚洲日产国产| 永久91嫩草亚洲精品人人| 三上悠亚久久精品| 日本美女一区二区| 在线观看中文av| av在线播放成人| 色撸撸在线视频| 亚洲尤物视频在线| 中文字幕在线日本| 91精品国产色综合久久久蜜香臀| 秋霞av鲁丝片一区二区| 在线观看亚洲视频| 亚洲欧美成人影院| 国产www精品| 欧洲大片精品免费永久看nba| 久久久久一区二区三区| 欧美jizz| 免费黄色日本网站| 国产精品一区二区久久不卡 | 亚洲视频在线一区观看| 四虎永久在线精品| 777xxx欧美| 六十路在线观看| 久久69精品久久久久久国产越南| 3d欧美精品动漫xxxx无尽| 97免费高清电视剧观看| 国产一区网站| 国产欧美日韩网站| 精品在线亚洲视频| 最近中文字幕免费视频| 亚洲综合色网站| 一级日韩一级欧美| 亚洲欧美一区二区三区在线| 在线观看电影av| 国产精品麻豆va在线播放| 看全色黄大色大片免费久久久| 永久免费精品视频网站| 久久精品电影| 天天插天天射天天干| 亚洲精品欧美在线| 97视频免费在线| 这里只有精品在线观看| 欧美第一视频| 久久国产精品久久精品国产| 国产精品vip| 日韩欧美中文在线视频| 国产精品电影一区二区| 91视频在线视频| 精品在线小视频| 黄视频免费在线看| 97人摸人人澡人人人超一碰| 久久久久美女| 亚洲美女性囗交| 国产精品久久久久久户外露出| 免费黄色av片| 亚洲视频一区二区| 欧美18av| 女人一区二区三区| 亚洲综合欧美| 中文幕无线码中文字蜜桃| 天天av天天翘天天综合网 | 亚洲成人av影片| 日韩精品一区二区视频| 老牛影视精品| 久久99精品国产一区二区三区| 激情欧美日韩一区| 亚洲 欧美 日韩在线| 亚洲成人tv网| 黄色一级大片在线免费看国产| 欧美激情第99页| 国产精品45p| 精品少妇一区二区三区在线| 337p粉嫩大胆噜噜噜噜噜91av | 欧美日韩在线观看一区二区| 中文日本在线观看| 成人精品在线观看| 91精品综合久久久久久久久久久| 在线观看日本一区二区| 国产精品久久久久久久第一福利 | 日韩男人的天堂| 日韩精品在线观看视频| 天天综合网天天| 午夜精品区一区二区三| 老司机精品视频导航| 9999热视频| 亚洲精品一区二区三区四区高清 | 在线精品视频一区二区三四| 成黄免费在线| 91久久综合亚洲鲁鲁五月天| 国内精品久久久久久久97牛牛 | 色噜噜久久综合| h视频在线播放| 91在线视频成人| 国色天香一区二区| 国产男女猛烈无遮挡a片漫画| 91国偷自产一区二区使用方法| 色网站免费在线观看| 99久久精品免费看国产四区| 亚洲免费影视| 国产jizz18女人高潮| 精品日韩在线观看| 免费福利视频一区二区三区| 一区二区三区的久久的视频| 国产成人三级在线观看| 国产又粗又爽视频| 日韩日本欧美亚洲| 国产精品网站在线看| 日本男人操女人| 一区二区三区小说| 色视频免费在线观看| 国产在线98福利播放视频| 在线不卡欧美| 伊人影院综合网| 亚洲成人激情在线| 91av一区| a级黄色一级片| 国产精品欧美综合在线| 天天操天天插天天射| 涩爱av在线播放一区二区| 中文字幕日韩av| 亚洲日本一区二区三区在线| 欧美成人xxxxx| 最新日韩av在线| 午夜在线视频免费| 国产免费成人av| 免费一级欧美片在线播放| 欧美做爰爽爽爽爽爽爽| 亚洲一区av在线播放| 九九热播视频在线精品6|