2022-12-30 ClientHttpRequestInterceptor 實現(xiàn)請求參數(shù)及返回值的跟蹤

1. ClientHttpRequestInterceptor 概述

函數(shù)式編程攔截器接口;攔截客戶端HTTP請求寻馏。這個接口的實現(xiàn)可以注冊到RestTemplate中借杰,以修改輸出的ClientHttpRequest和/或傳入的ClientHttpResponse随抠。

攔截器的主要入口點是截取(HttpRequest, byte[]显熏, ClientHttpRequestExecution)揽碘。

2. 實現(xiàn)ClientHttpRequestInterceptor 參數(shù)攔截

logRequestDetails 處理請求參數(shù)
logResponseDetails 處理返回參數(shù)

@Slf4j
public class LoggingInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(
            HttpRequest request, byte[] body,
            ClientHttpRequestExecution execution) throws IOException {

        //打印請求明細
        logRequestDetails(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        //打印響應明細
        logResponseDetails(response);

        return response;
    }
     /**
      請求參數(shù)
    */
    private void logRequestDetails(HttpRequest request, byte[] body) {

        log.debug("Headers: {}", request.getHeaders());
        log.debug("body: {}", new String(body, Charsets.UTF_8));
        log.debug("{}:{}", request.getMethod(), request.getURI());
    }

    private void logResponseDetails(ClientHttpResponse response) throws IOException {

        log.debug("Status code  : {}", response.getStatusCode());
        log.debug("Status text  : {}", response.getStatusText());
        log.debug("Headers      : {}", response.getHeaders());
        log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));

    }
}

3. 定義日志攔截切面 RequestLogAspect

定義切點openrpc 該切點就是需要攔截請求參數(shù)與返回參數(shù)的地方

    @Pointcut("execution(* com.cxist.open.controller.open..*(..))")
    public void openrpc() {
    }

4. 切點織入邏輯蝉娜;

doAround 方法以舒,捕獲請求參數(shù)

 @Around("openrpc()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

調(diào)用異常的時候使用,捕獲異常信息

    @AfterThrowing(pointcut = "openrpc()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {

5. 具體實現(xiàn)如下霎肯;

@Component
@Aspect
@Slf4j
public class RequestLogAspect {

    private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);
    private static Integer SUCCESS = 1;
    private static Integer FAIL = 0;
    private static String TOKEN = "token";
    @Autowired
    InterfaceDetailLogService interfaceDetailLogService;
    @Autowired
    LogClient logClient;
    @Resource(name = "openApiThreadExecutor")
    ThreadPoolTaskExecutor threadPoolTaskExecutor;


    public static void main(String[] args) {

        System.out.println("DateUtil.date = " + DateUtil.date());
        System.out.println("DateUtil.current = " + DateUtil.current());
        System.out.println("DateUtil.currentSeconds = " + DateUtil.currentSeconds());
    }

    /**
     * Controller層都被訪問
     */
    @Pointcut("execution(* com.cxist.open.controller.open..*(..))")
    public void openrpc() {
    }

    @Around("openrpc()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Date requestDateTime = DateUtil.date();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        Object result = proceedingJoinPoint.proceed();
        /**若發(fā)生異常則以下不再執(zhí)行擎颖,可以在doAfterThrow 記錄**/
        InterfaceDetailLogDTO logDTO = new InterfaceDetailLogDTO();
        logDTO.setIp(request.getRemoteAddr());
        logDTO.setUrl(request.getRequestURL().toString());
        logDTO.setRequestMethod(request.getMethod());
        logDTO.setRespondBody(result.toString());

        logDTO.setRequestTime(requestDateTime);
        logDTO.setRespondTime(DateUtil.date());
        logDTO.setStatus(SUCCESS);
        Map<String, Object> paramMap = getRequestParamsByProceedingJoinPoint(proceedingJoinPoint);
        logDTO.setRequestBody(JSONUtil.toJsonStr(paramMap));
        String token = paramMap.get(TOKEN) != null ? paramMap.get(TOKEN).toString() : "";
        MultiValueMap<String, String> headerMap = new LinkedMultiValueMap<>();
        headerMap.add(org.apache.http.HttpHeaders.AUTHORIZATION, token);
        try {
            //從主線程中獲得所有request數(shù)據(jù)
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            threadPoolTaskExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    log.info("--------------------doAround-------------------------");
                    Result saveFlag = logClient.save(headerMap, logDTO);
                    log.info("saveFlag.code ===" + saveFlag.getCode());
                }
            });
        } catch (Exception e) {
            log.error("e ===== e" + e);
        }
        LOGGER.info("Request Info      : {}", JSON.toJSONString(logDTO));
        return result;
    }

    @AfterReturning(returning = "ret", pointcut = "openrpc()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 處理完請求,返回內(nèi)容
        log.info("RESPONSE : " + ret);

    }

    @AfterThrowing(pointcut = "openrpc()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
        InterfaceDetailLogDTO logDTO = null;
        MultiValueMap<String, String> headerMap = null;
        /**異步Feign調(diào)用观游,傳遞request 請求上下文**/
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        logDTO = new InterfaceDetailLogDTO();
        logDTO.setIp(request.getRemoteAddr());
        logDTO.setUrl(request.getRequestURL().toString());
        logDTO.setRequestMethod(request.getMethod());
        Map<String, Object> paramsMap = getRequestParamsByJoinPoint(joinPoint);
        String token = paramsMap.get(TOKEN) != null ? paramsMap.get(TOKEN).toString() : "";
        headerMap = new LinkedMultiValueMap<>();
        headerMap.add(org.apache.http.HttpHeaders.AUTHORIZATION, token);
        logDTO.setRequestBody(JSONUtil.toJsonStr(paramsMap));
        logDTO.setRequestTime(DateUtil.date());
        logDTO.setRespondTime(DateUtil.date());
        /**異常信息過程搂捧,F(xiàn)eign請求的時候會被拒絕**/
        if (e.getMessage().length() > 300) {
            logDTO.setErrorMessage(e.getMessage().substring(0, 300));
        } else {
            logDTO.setErrorMessage(e.getMessage());
        }

        logDTO.setStatus(FAIL);
        InterfaceDetailLogEntity entity = new InterfaceDetailLogEntity();
        BeanUtils.copyProperties(logDTO, entity);
        try {
            InterfaceDetailLogDTO finalLogDTO = logDTO;
            MultiValueMap<String, String> finalHeaderMap = headerMap;
            //從主線程中獲得所有request數(shù)據(jù)
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            threadPoolTaskExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    /**異步Feign調(diào)用,傳遞request 請求上下文**/
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    log.info("------------------doAfterThrow---------------------------");
                    Result saveFlag = logClient.save(finalHeaderMap, finalLogDTO);
                    log.info("saveFlag.code ===" + saveFlag.getCode());
                }
            });

        } catch (Exception ex) {
            log.error("e ===== ex" + ex);
        }
    }

    private InterfaceDetailLogEntity getInterfaceDetailLogEntity(JoinPoint joinPoint, RuntimeException e, HttpServletRequest request) {
        InterfaceDetailLogEntity entity = new InterfaceDetailLogEntity();
        entity.setIp(request.getRemoteAddr());
        entity.setUrl(request.getRequestURL().toString());
        entity.setRequestMethod(request.getMethod());
        entity.setRequestBody(JSONUtil.toJsonStr(getRequestParamsByJoinPoint(joinPoint)));
        entity.setRequestTime(null);
        entity.setRespondTime(DateUtil.date());
        entity.setErrorMessage(e.getMessage());
        entity.setStatus(FAIL);
        return entity;
    }

    /**
     * 獲取入?yún)?     *
     * @param proceedingJoinPoint
     * @return
     */
    private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        //參數(shù)名
        String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        //參數(shù)值
        Object[] paramValues = proceedingJoinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        //參數(shù)名
        String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        //參數(shù)值
        Object[] paramValues = joinPoint.getArgs();

        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
        Map<String, Object> requestParams = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];

            //如果是文件對象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                value = file.getOriginalFilename();  //獲取文件名
            }

            requestParams.put(paramNames[i], value);
        }

        return requestParams;
    }

    @Data
    public class RequestErrorInfo {
        private String ip;
        private String url;
        private String httpMethod;
        private String classMethod;
        private Object requestParams;
        private RuntimeException exception;
    }

6 總結(jié)

1.LoggingInterceptor
實現(xiàn) implements ClientHttpRequestInterceptor 封裝請求懂缕,及返回參數(shù)

···
@Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {

    //打印請求明細
    logRequestDetails(request, body);
    ClientHttpResponse response = execution.execute(request, body);
    //打印響應明細
    logResponseDetails(response);

    return response;
}

···

2.RequestLogAspect
定義攔截切面

3. 日志記錄服務類允跑,記錄日志

單獨的日志處理邏輯

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市搪柑,隨后出現(xiàn)的幾起案子聋丝,更是在濱河造成了極大的恐慌,老刑警劉巖工碾,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弱睦,死亡現(xiàn)場離奇詭異,居然都是意外死亡渊额,警方通過查閱死者的電腦和手機况木,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旬迹,“玉大人火惊,你說我怎么就攤上這事”伎眩” “怎么了屹耐?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長椿猎。 經(jīng)常有香客問我惶岭,道長寿弱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任俗他,我火速辦了婚禮脖捻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兆衅。我一直安慰自己,他們只是感情好嗜浮,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布羡亩。 她就那樣靜靜地躺著,像睡著了一般危融。 火紅的嫁衣襯著肌膚如雪畏铆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天吉殃,我揣著相機與錄音辞居,去河邊找鬼。 笑死蛋勺,一個胖子當著我的面吹牛瓦灶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抱完,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼贼陶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了巧娱?” 一聲冷哼從身側(cè)響起碉怔,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎禁添,沒想到半個月后撮胧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡老翘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年芹啥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酪捡。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡叁征,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逛薇,到底是詐尸還是另有隱情捺疼,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布永罚,位于F島的核電站啤呼,受9級特大地震影響卧秘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜官扣,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一翅敌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惕蹄,春花似錦蚯涮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泪蔫,卻和暖如春棒旗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撩荣。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工铣揉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人餐曹。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓逛拱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親凸主。 傳聞我的和親對象是個殘疾皇子橘券,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容