国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

springboot 日志記錄接口的請求參數(shù)和響應(yīng)結(jié)果的兩種方式-攔截器和切面(具體代碼)

這篇具有很好參考價值的文章主要介紹了springboot 日志記錄接口的請求參數(shù)和響應(yīng)結(jié)果的兩種方式-攔截器和切面(具體代碼)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

springboot 日志記錄接口的請求參數(shù)和響應(yīng)結(jié)果的兩種方式-攔截器和切面(具體代碼)

前言:在生產(chǎn)中如果出現(xiàn)問題,我們想要查看日志,某個時間段用戶調(diào)用接口的請求參數(shù)和響應(yīng)的返回結(jié)果,通過日志來推測下用戶當(dāng)時做了什么操作。日志記錄接口的請求參數(shù)和響應(yīng)結(jié)果有利于我們排查生產(chǎn)的問題,但是也會給系統(tǒng)帶來內(nèi)存性能的問題。所以我們需要權(quán)衡其中的利弊來選擇,下面就是記錄日志兩種方式的具體代碼。

一、使用切面(推薦使用這種,簡單)
@Component
@Aspect
@Slf4j
public class ApiLogAspect {

    @Pointcut("execution(* com.xl.finance.module..controller..*.*(..))")
    public void controller() {
    }

    @Around("controller()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long startTime = System.currentTimeMillis();
        Long userId = WebUtils.getId();
        String userName = WebUtils.getName();
        String requestUrl = WebUtils.getRequestUrl();
        requestUrl = new URL(requestUrl).getPath();
        String requestParam = getMethodArgs(point);
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        String responseParam = handlerResult(result);
        log.info("requestUrl:{} userId:{},userName:{} requestParam:{},responseParam:{},rtt:{}ms", requestUrl, userId, userName, requestParam, responseParam, endTime - startTime);
        return result;
    }

    private String getMethodArgs(JoinPoint point) {
        Object[] args = point.getArgs();
        if (args == null || args.length == 0) {
            return "";
        }
        try {
            Map<String, Object> params = new HashMap<>();
            String[] parameterNames = ((MethodSignature) point.getSignature()).getParameterNames();
            for (int i = 0; i < parameterNames.length; i++) {
                Object arg = args[i];
                // 過濾不能轉(zhuǎn)換成JSON的參數(shù)
                if ((arg instanceof ServletRequest) || (arg instanceof ServletResponse)) {
                    continue;
                } else if ((arg instanceof MultipartFile)) {
                    arg = arg.toString();
                }
                params.put(parameterNames[i], arg);
            }
            return JSONObject.toJSONString(params);
        } catch (Exception e) {
            log.error("接口出入?yún)⑷罩敬蛴∏忻嫣幚碚埱髤?shù)異常", e);
        }
        return Arrays.toString(args);
    }

    /**
     * 返回結(jié)果簡單處理
     * 1)把返回結(jié)果轉(zhuǎn)成String,方便輸出。
     * 2)返回結(jié)果太長則截取(最多3072個字符),方便展示。
     *
     * @param result 原方法調(diào)用的返回結(jié)果
     * @return 處理后的
     */
    private String handlerResult(Object result) {
        if (result == null) {
            return null;
        }
        String resultStr;
        try {
            if (result instanceof String) {
                resultStr = (String) result;
            } else {
                resultStr = JSONObject.toJSONString(result);// 如果返回結(jié)果非String類型,轉(zhuǎn)換成JSON格式的字符串
            }

            if (resultStr.length() > 3072) {
                resultStr = resultStr.substring(0, 3072);
            }
        } catch (Exception e) {
            resultStr = result.toString();
            log.error("接口出入?yún)⑷罩敬蛴∏忻嫣幚矸祷貐?shù)異常", e);
        }
        return resultStr;
    }
}
二、使用攔截器

本來以為這種方式只要自定義攔截器繼承HandlerInterceptorAdapter類重寫preHandle() 和 afterCompletion()就可以了。沒想到還是遇到挺多坑。

請求參數(shù):request.getParameterMap()可以獲取到請求參數(shù),但是如果接口是使用@RequestBody,就會發(fā)現(xiàn)得不到值。

響應(yīng)結(jié)果:這是比較蛋疼一點(diǎn),我?guī)缀醪榱藃esponse的所有方法,都發(fā)現(xiàn)沒法得到接口響應(yīng)結(jié)果。

下面直接貼代碼。

2.1 實(shí)現(xiàn)Filter類

流是一次性的,為了防止在日志中讀取到流的請求參數(shù),影響實(shí)際請求的接口

@Component
@WebFilter(filterName = "HttpServletRequestFilter", urlPatterns = "/")
@Order(10000)
public class HttpServletRequestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(servletRequest instanceof HttpServletRequest) {
            requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
        }
        //獲取請求中的流如何,將取出來的字符串,再次轉(zhuǎn)換成流,然后把它放入到新request對象中
        // 在chain.doFiler方法中傳遞新的request對象
        if(null == requestWrapper) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
2.2 繼承RequestWrapper類
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    private final String body;

    public RequestWrapper(HttpServletRequest request) {
        super(request);

        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try (InputStream inputStream = request.getInputStream()) {
            if(inputStream != null){
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } catch (Exception e) {
            log.error("RequestWrapper read error :{}",e.getMessage());
        } finally {
            IoUtil.close(bufferedReader);
        }
        body = stringBuilder.toString();
    }

    /**
     * 將getInputStream重新,讓它能重復(fù)獲取到body里的內(nèi)容,這樣才不會影響后續(xù)的流程
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

    /**
     * 重寫獲取 字符流的方式
     * @return
     */
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream(), StandardCharsets.UTF_8));
    }


    /**
     * 獲取body
     * @return
     */
    public String getBody() {
        return this.body;
    }
}
2.3 實(shí)現(xiàn)ResponseBodyAdvice類

在實(shí)現(xiàn)ResponseBodyAdvice,就可以獲取到接口響應(yīng)的結(jié)果來打印日志,但是為了統(tǒng)一在自定義的攔截器處理日志,這里把響應(yīng)的結(jié)果存在response的一個屬性里。文章來源地址http://www.zghlxwxcb.cn/news/detail-785741.html

@ControllerAdvice
public class InterceptResponse implements ResponseBodyAdvice<Object> {

    private static final Integer MAX_LENGTH = 1000;

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        String result ;
        if(body instanceof JsonResult){
            JsonResult jsonResult = (JsonResult) body;
            result = JSON.toJSONString(jsonResult);
        }else{
            result = JSON.toJSONString(body);
        }
        Integer length = result.length() > MAX_LENGTH ? MAX_LENGTH :  result.length();
        result = result.substring(0,length);
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession httpSession = httpServletRequest.getSession(true);
        //放到緩存里,以便于可以在HandlerInterceptor攔截里取出并打印出返回結(jié)果
        httpSession.setAttribute("body", result);
        return body;
    }
}
2.4 新建定義攔截器繼承HandlerInterceptorAdapter
@Component
@Slf4j
public class LogMonitorInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestParams = StringUtils.EMPTY;
        if (request instanceof RequestWrapper) {
            requestParams = ((RequestWrapper) request).getBody();
        }
        if(StrUtil.isEmpty(requestParams)){
            requestParams = JSON.toJSONString(request.getParameterMap());
        }
        log.info("request:  uri:{} , type:{} , ip:{}, operatorId:{}, operatorName:{}, params:{}",
                request.getRequestURI(),request.getMethod(),request.getRemoteAddr(),
                WebUtils.getId(),WebUtils.getUsername(), requestParams );
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HttpSession httpSession = request.getSession();
        String result = (String) httpSession.getAttribute("body");

        log.info("response:  url:{} , type:{} , ip:{}, operatorId:{}, operatorName:{}, result:{}",
                request.getRequestURI(),request.getMethod(),request.getRemoteAddr(),
                WebUtils.getId(),WebUtils.getUsername(), result );
    }
}
2.5 實(shí)現(xiàn)WebMvcConfigurer
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private LogMonitorInterceptor logMonitorInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // 接口操作日志攔截器
        registry.addInterceptor(logMonitorInterceptor);
    }
}

到了這里,關(guān)于springboot 日志記錄接口的請求參數(shù)和響應(yīng)結(jié)果的兩種方式-攔截器和切面(具體代碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Springboot調(diào)整接口響應(yīng)返回時長詳解(解決響應(yīng)超時問題)_springboot設(shè)置請求超時時間

    1、配置Http會話超時 可以通過兩種方式為Spring Boot應(yīng)用程序 配置HTTP會話超時 。 1.1 application.properties中配置會話超時 最簡單的方法是在你的application.properties中加入?yún)?shù) server.servlet.session.timeout 。 還要注意的是, Tomcat不允許你將超時時間設(shè)置得少于60秒 。 1.2 以程序方式配置會

    2024年04月27日
    瀏覽(114)
  • python requests.get發(fā)送Http請求響應(yīng)結(jié)果亂碼、Postman請求結(jié)果正常

    最近在寫爬蟲程序,自己復(fù)制網(wǎng)頁http請求的url、頭部,使用python requests和postman分別請求,結(jié)果使用postman發(fā)送http get請求,可以得到正常的json數(shù)據(jù),但是使用python的requests發(fā)送則接受到亂碼,response.text的內(nèi)容是: response.content的內(nèi)容是: 十分費(fèi)解,于是網(wǎng)上搜索了相關(guān)內(nèi)容,

    2024年01月24日
    瀏覽(32)
  • SpringBoot初級開發(fā)--服務(wù)請求(GET/POST)所有參數(shù)的記錄管理(8)

    SpringBoot初級開發(fā)--服務(wù)請求(GET/POST)所有參數(shù)的記錄管理(8)

    ??服務(wù)端在定位錯誤的時候,有時候要還原現(xiàn)場,這就要把當(dāng)時的所有入?yún)?shù)都能記錄下來,GET還好說,基本NGINX都會記錄。但是POST的請求參數(shù)基本不會被記錄,這就需要我們通過一些小技巧來記錄這些參數(shù),放入日志,這里我們通過自定義攔截器來做這個操作。 我們緊

    2024年02月09日
    瀏覽(29)
  • Spring/SpringBoot 過濾器修改、獲取http 請求request中的參數(shù) 和 response返回值,比如修改請求體和響應(yīng)體的字符編碼

    通過自定義filter,RequestWrapper,ResponseWrapper 處理請求和響應(yīng)數(shù)據(jù),比如修改請求體和響應(yīng)體的字符編碼 1.request 和 response 中的數(shù)據(jù)都是 存在流中的(緩存中)獲取一次就沒有了,需要重新寫回去。所以需要兩個包裝類分別繼承HttpServletRequestWrapper 和 HttpServletResponseWrapper 對 r

    2024年02月15日
    瀏覽(37)
  • Python發(fā)送Post請求及解析響應(yīng)結(jié)果

    Python發(fā)送Post請求及解析響應(yīng)結(jié)果

    有時候遇到請求url中有很多參數(shù)。 1.1 示例1 accounts和pwd請到http://shop-xo.hctestedu.com/注冊。 執(zhí)行結(jié)果: 1.2 示例2 使用不定長參數(shù) params,將url中需要的參數(shù)單獨(dú)封裝。 執(zhí)行結(jié)果: 用type()查看response.text的類型,是str 執(zhí)行結(jié)果: 用type()查看response.json()的類型,是dict 執(zhí)行結(jié)果:

    2023年04月24日
    瀏覽(25)
  • postman獲取請求響應(yīng)結(jié)果并設(shè)置到全局變量中

    postman獲取請求響應(yīng)結(jié)果并設(shè)置到全局變量中

    做接口測試中,經(jīng)常遇到就是我們首先要去獲取一個請求響應(yīng)返回的參數(shù)(這個返回值是我們需要的),這個接口我們跑通了返回值也有了,那么如何去將它提取出來并寫入到全局變量里去呢? 可通過返回值的層級一步一步的獲取到想要的返回值并保存到變量里面,如下:

    2024年02月15日
    瀏覽(47)
  • SpringCloud Gateway 打印請求響應(yīng)日志

    SpringCloud Gateway 打印請求響應(yīng)日志

    version spring-cloud 2021.0.1 spring-boot 2.6.3 spring-cloud-alibaba 2021.0.1.0 網(wǎng)關(guān)不是基于springmvc的,而是基于webflux去做的 SpringCloudGateway中Post請求參數(shù)只能讀取一次 這是因?yàn)镚ateway默認(rèn)使用的是SpringWebflux,解決這個問題需要容重新構(gòu)造一個request來替換原先的request CacheBodyGlobalFilter這個全局過

    2024年02月02日
    瀏覽(97)
  • 【Postman】批量請求接口并存儲返回結(jié)果

    【Postman】批量請求接口并存儲返回結(jié)果

    摘要: 這是一篇0基礎(chǔ)工具文檔 使用:postman 參數(shù)化、測試斷言、存儲測試結(jié)果 等幾項(xiàng)功能 實(shí)現(xiàn):對接口進(jìn)行批量請求,并存儲結(jié)果 先創(chuàng)建一個集合,再在集合中創(chuàng)建接口請求 這樣執(zhí)行集合的時候,就可以通過參數(shù)化的文本文件,實(shí)現(xiàn)對同一接口的批量執(zhí)行 貼圖: 如圖輸

    2024年01月21日
    瀏覽(25)
  • 鴻蒙開發(fā),使用http返回的響應(yīng)數(shù)據(jù)無法正常獲取 ,利用hilog打印日志一直結(jié)果是object或者代碼憑空消失,根本沒有打印日志(靈異事件???)

    鴻蒙開發(fā),使用http返回的響應(yīng)數(shù)據(jù)無法正常獲取 ,利用hilog打印日志一直結(jié)果是object或者代碼憑空消失,根本沒有打印日志(靈異事件???)

    這里簡述項(xiàng)目相關(guān)背景:前后端分離項(xiàng)目,使用鴻蒙做前端,后端SpringBoot寫好接口(通過商品分類id查詢商品列表),鴻蒙前端頁面使用Tabs組件導(dǎo)航,展示商品分類,點(diǎn)擊分類標(biāo)簽,查詢后端接口,返回對應(yīng)分類商品列表數(shù)據(jù) 項(xiàng)目場景:鴻蒙開發(fā),使用http返回的響應(yīng)數(shù)據(jù)無

    2024年04月27日
    瀏覽(15)
  • 請求響應(yīng)-實(shí)體參數(shù)的接受

    請求響應(yīng)-實(shí)體參數(shù)的接受

    簡單實(shí)體對象: 請求參數(shù)名與形參屬性對象名相同,定義pojo接受即可,將數(shù)據(jù)封裝到實(shí)體類中 實(shí)體類代碼如下: 控制類代碼如下: ? ?postman中發(fā)送請求、idea接受并處理請求結(jié)果如下: 復(fù)雜實(shí)體參數(shù)接受?(一個實(shí)體類中包含另一個實(shí)體類作為對象,即 對象的組合 ) 參數(shù)

    2024年02月16日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包