SpringBoot怎么監(jiān)聽所有請(qǐng)求(保存api日志)

功能描述

在SpringBoot中如要實(shí)現(xiàn)記錄接口被調(diào)用的頻率和生成api日志宛琅,以便查看接口使用情況闪水,那么監(jiān)聽所有api請(qǐng)求的功能就誕生了报破。

功能實(shí)現(xiàn)

一任连、自定義request繼承HttpServletRequestWrapper

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.alibaba.fastjson.JSONObject;

public class MyRequestWrapper extends HttpServletRequestWrapper {
    private byte[] body;
    private ServletInputStreamWrapper inputStreamWrapper;
    private Object requestBody;

    public MyRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        int le = request.getContentLength();
        if (le > 0) {
            body = new byte[le];
            request.getInputStream().read(body);
            requestBody = JSONObject.parse(body, 0, body.length, Charset.forName("UTF-8").newDecoder(), 0);
        } else {
            body = new byte[0];
            requestBody = "";
        }
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body);
        this.inputStreamWrapper = new ServletInputStreamWrapper(byteArrayInputStream);
        resetInputStream();
    }

    public String getRequestBody() {
        return String.valueOf(requestBody);
    }

    public void setRequestBody(Object requestBody) {
        this.requestBody = requestBody;
    }

    private void resetInputStream() {
        this.inputStreamWrapper.setInputStream(new ByteArrayInputStream(this.body != null ? this.body : new byte[0]));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return this.inputStreamWrapper;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.inputStreamWrapper));
    }

    private static class ServletInputStreamWrapper extends ServletInputStream {

        private InputStream inputStream;

        public ServletInputStreamWrapper(InputStream inputStream) {
            super();
            this.inputStream = inputStream;
        }

        public InputStream getInputStream() {
            return inputStream;
        }

        public void setInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public boolean isFinished() {
            return true;
        }

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

        @Override
        public void setReadListener(ReadListener readListener) {

        }

        @Override
        public int read() throws IOException {
            return this.inputStream.read();
        }
    }
}

二蚤吹、自定義response繼承HttpServletResponseWrapper

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import com.alibaba.fastjson.JSONObject;
public class MyResponseWrapper extends HttpServletResponseWrapper {
    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    private HttpServletResponse response;
    private Object reponseBody;

    public MyResponseWrapper(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    public String getResponseBody() {
        byte[] bytes = byteArrayOutputStream.toByteArray();
        reponseBody = JSONObject.parse(bytes, 0, bytes.length, Charset.forName("UTF-8").newDecoder(), 0);
        return String.valueOf(reponseBody);
    }

    @Override
    public ServletOutputStream getOutputStream() {
        return new ServletOutputStreamWrapper(this.byteArrayOutputStream, this.response);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        return new PrintWriter(
                new OutputStreamWriter(this.byteArrayOutputStream, this.response.getCharacterEncoding()));
    }

    private static class ServletOutputStreamWrapper extends ServletOutputStream {

        private ByteArrayOutputStream outputStream;
        private HttpServletResponse response;

        public ServletOutputStreamWrapper(ByteArrayOutputStream outputStream, HttpServletResponse response) {
            this.outputStream = outputStream;
            this.response = response;
        }

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

        @Override
        public void setWriteListener(WriteListener listener) {

        }

        @Override
        public void write(int b) throws IOException {
            this.outputStream.write(b);
        }

        @Override
        public void flush() throws IOException {
            if (!this.response.isCommitted()) {
                byte[] body = this.outputStream.toByteArray();
                ServletOutputStream outputStream = this.response.getOutputStream();
                outputStream.write(body);
                outputStream.flush();
            }
        }
    }

三、新建一個(gè)類RequestWrapperFilter繼承OncePerRequestFilter随抠,重寫doFilterInternal()方法裁着,具體如下。

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.lkl.web.maven1.api.help.MyRequestWrapper;
import com.lkl.web.maven1.api.help.MyResponseWrapper;

/**
 * 監(jiān)聽所有請(qǐng)求
 * 
 * @author Administrator
 *
 */
@Component
public class RequestWrapperFilter extends OncePerRequestFilter {
    private MyRequestWrapper requestWrapper;
    private MyResponseWrapper reponseWrapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        try {
            requestWrapper = new MyRequestWrapper(request);
            reponseWrapper = new MyResponseWrapper(response);
            filterChain.doFilter(requestWrapper, reponseWrapper);
            String requestBody = requestWrapper.getRequestBody();
            String reponseBody = reponseWrapper.getResponseBody();
                        //做你想做的事情
        } catch (Exception e) {
                        //失敗時(shí)拱她,默認(rèn)即可
            filterChain.doFilter(request, response);
        }
    }
}

簡(jiǎn)單總結(jié)

1.自定義reques和response無非就是想從流中拿到byte[]二驰,因?yàn)樵趏utputStream和inputStream中read()后再次read()會(huì)讀不到數(shù)據(jù)。
2.有時(shí)候會(huì)因?yàn)楦鞣N請(qǐng)求體或響應(yīng)體為空導(dǎo)致doFilterInternal失敗秉沼,因此加個(gè)try catch塊桶雀。
3.注意RequestWrapperFilter中標(biāo)注@Component。
4.保存日志有可能是高頻率的io操作唬复,可通過定時(shí)器來定時(shí)矗积,選擇性保存日志。

以上都是我的個(gè)人理解敞咧,如有不足棘捣,請(qǐng)?jiān)谠u(píng)論區(qū)指出。不勝感激休建。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乍恐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丰包,更是在濱河造成了極大的恐慌,老刑警劉巖壤巷,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邑彪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡胧华,警方通過查閱死者的電腦和手機(jī)寄症,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門宙彪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人有巧,你說我怎么就攤上這事释漆。” “怎么了篮迎?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵男图,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我甜橱,道長(zhǎng)逊笆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任岂傲,我火速辦了婚禮难裆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘镊掖。我一直安慰自己乃戈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布亩进。 她就那樣靜靜地躺著症虑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镐侯。 梳的紋絲不亂的頭發(fā)上侦讨,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音苟翻,去河邊找鬼韵卤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛崇猫,可吹牛的內(nèi)容都是我干的沈条。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼诅炉,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蜡歹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涕烧,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤月而,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后议纯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體父款,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了憨攒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片世杀。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肝集,靈堂內(nèi)的尸體忽然破棺而出瞻坝,到底是詐尸還是另有隱情,我是刑警寧澤杏瞻,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布所刀,位于F島的核電站,受9級(jí)特大地震影響伐憾,放射性物質(zhì)發(fā)生泄漏勉痴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一树肃、第九天 我趴在偏房一處隱蔽的房頂上張望蒸矛。 院中可真熱鬧,春花似錦胸嘴、人聲如沸雏掠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乡话。三九已至,卻和暖如春耳奕,著一層夾襖步出監(jiān)牢的瞬間绑青,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工屋群, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闸婴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓芍躏,卻偏偏與公主長(zhǎng)得像邪乍,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子对竣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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