SpringBoot2.x 利用Filter打印pv日志

ELK日志收集時(shí),我們需要在Filter中統(tǒng)一的打印請(qǐng)求報(bào)文虫碉。便于在Kibina中查詢

遇見兩個(gè)問題:

  1. Servlet流是單向的寓免,在Filter中解析那么后續(xù)會(huì)出現(xiàn)異常;
  2. 對(duì)于請(qǐng)求的不同Method(GET/POST等)如何在Filter中解析為字符串盹沈;

解決方案:

1.1 將單向流設(shè)置為可重復(fù)讀流

定義一個(gè)優(yōu)先級(jí)高的Filter龄章,實(shí)現(xiàn)流的轉(zhuǎn)換吃谣。

@Order(Integer.MIN_VALUE + 1)
@Slf4j
@Service
public class RepeatableFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        HttpServletRequest req = (HttpServletRequest) request;

        String contentType = req.getContentType();
        //對(duì)于文件上傳,直接放行做裙。
        if (contentType != null && contentType.toLowerCase().contains("multipart")) {
            //直接放行
            chain.doFilter(request, response);
            return;
        }
        //重寫流對(duì)象
        if (!(request instanceof BackupRequestWrapper)) {
            req = new BackupRequestWrapper(req);
        }
        chain.doFilter(req, response);
    }
}

原理是:繼承HttpServletRequestWrapper岗憋,內(nèi)部使用ByteArrayInputStream來實(shí)現(xiàn)流的可重復(fù)讀。

public class BackupRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] buffer;

    public BackupRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        InputStream is = request.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int read;
        byte[] buff = new byte[1024];
        while ((read = is.read(buff)) > 0) {
            baos.write(buff, 0, read);
        }
        this.buffer = baos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new BufferedServletInputStream(this.buffer);
    }

    // 對(duì)外提供讀取流的方法
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

class BufferedServletInputStream extends ServletInputStream {
    private final ByteArrayInputStream inputStream;

    public BufferedServletInputStream(byte[] buffer) {
        this.inputStream = new ByteArrayInputStream(buffer);
    }

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

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

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return inputStream.read(b, off, len);
    }

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

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

    @Override
    public void setReadListener(ReadListener readListener) {

    }
}

1.2 將request參數(shù)讀取為JSON

提供一個(gè)工具類:來針對(duì)GET/POST請(qǐng)求來單獨(dú)處理锚贱。

@Slf4j
public class RequestJsonUtil {


    /***
     * 獲取 request 中 json 字符串的內(nèi)容
     *
     * @return : <code>byte[]</code>
     * @throws IOException
     */
    public static String getRequestJsonString(HttpServletRequest request) throws IOException {
        String submitMehtod = request.getMethod();
        // GET
        String s = new String("".getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8).replaceAll("%22", "\"");
        if (submitMehtod.equals("GET")) {
            if (StringUtils.isNotEmpty(request.getQueryString())) {
                return new String(request.getQueryString().getBytes(StandardCharsets.ISO_8859_1),
                        StandardCharsets.UTF_8).replaceAll("%22", "\"");
            }
            return s;
        }


        // POST
        String requestString = getRequestPostStr(request);

        if (StringUtils.isNotBlank(requestString)) {
            return requestString;
        }

        if (StringUtils.isNotEmpty(request.getQueryString())) {
            return new String(request.getQueryString().getBytes(StandardCharsets.ISO_8859_1),
                    StandardCharsets.UTF_8).replaceAll("%22", "\"");
        }
        return s;
    }

    public static String getRequestJsonStringNoException(HttpServletRequest request) {
        try {
            return getRequestJsonString(request);
        } catch (IOException e) {
            log.error("獲取請(qǐng)求參數(shù)異常", e);
        }
        return null;
    }

    /**
     * 描述:獲取 post 請(qǐng)求的 byte[] 數(shù)組
     * <pre>
     * 舉例:
     * </pre>
     */
    public static byte[] getRequestPostBytes(HttpServletRequest request)
            throws IOException {
        int contentLength = request.getContentLength();
        if (contentLength < 0) {
            return null;
        }
        byte[] buffer;
        buffer = new byte[contentLength];
        int i = 0;
        while (i < contentLength) {
            int readlen = request.getInputStream().read(buffer, i,
                    contentLength - i);
            if (readlen == -1) {
                break;
            }
            i += readlen;
        }
        return buffer;
    }

    /**
     * 描述:獲取 post 請(qǐng)求內(nèi)容
     * <pre>
     * 舉例:
     * </pre>
     */
    public static String getRequestPostStr(HttpServletRequest request)
            throws IOException {
        byte[] buffer = getRequestPostBytes(request);
        String charEncoding = request.getCharacterEncoding();
        if (charEncoding == null) {
            charEncoding = "UTF-8";
        }
        if (null == buffer) {
            return null;
        } else {
            return new String(buffer, charEncoding);
        }
    }

}

然后在利用Filter打印數(shù)據(jù)仔戈,然后配置logback.xml等日志配置文件來將這個(gè)pv Filter類的日志輸出打印到一個(gè)單獨(dú)日志文件中。使用Elk來解析收集展示pv日志拧廊。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末监徘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吧碾,更是在濱河造成了極大的恐慌凰盔,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倦春,死亡現(xiàn)場(chǎng)離奇詭異户敬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)溅漾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門山叮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人添履,你說我怎么就攤上這事屁倔。” “怎么了暮胧?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵锐借,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我往衷,道長(zhǎng)钞翔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任席舍,我火速辦了婚禮布轿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘来颤。我一直安慰自己汰扭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布福铅。 她就那樣靜靜地躺著萝毛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滑黔。 梳的紋絲不亂的頭發(fā)上笆包,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天环揽,我揣著相機(jī)與錄音,去河邊找鬼庵佣。 笑死歉胶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秧了。 我是一名探鬼主播跨扮,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼验毡!你這毒婦竟也來了衡创?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤晶通,失蹤者是張志新(化名)和其女友劉穎璃氢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狮辽,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡一也,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了喉脖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椰苟。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖树叽,靈堂內(nèi)的尸體忽然破棺而出舆蝴,到底是詐尸還是另有隱情,我是刑警寧澤题诵,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布洁仗,位于F島的核電站,受9級(jí)特大地震影響性锭,放射性物質(zhì)發(fā)生泄漏赠潦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一草冈、第九天 我趴在偏房一處隱蔽的房頂上張望她奥。 院中可真熱鬧,春花似錦怎棱、人聲如沸方淤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至你踩,卻和暖如春诅岩,著一層夾襖步出監(jiān)牢的瞬間讳苦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工吩谦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸳谜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓式廷,卻偏偏與公主長(zhǎng)得像咐扭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子滑废,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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