在SpringMVC中打印完整的AccessLog

轉載請注明來源 賴賴的博客

導語

總要造一下輪子才知道別人的輪子有多厲害

最近筆者遇到一個問題:在SpringMVC框架下,沒有直接的方式可以打印完整的AccessLog,因為request body 和response body是通過inputstream和outputstream來封裝的,在使用過很多人提供的方式后,發現效果總是不盡人意,所以自己摸索了一下,完成了這個功能

請慎重在生產環境使用這個功能,因為打印完整的AccessLog是一個很大的消耗

項目工程目錄結構和代碼獲取地址

https://github.com/laiyijie/spring-access-log-filter

詳解核心類AccessLogFilter

完整代碼如下:

public class AccessLogFilter extends OncePerRequestFilter {
    private static final Logger logger = LogManager.getLogger(AccessLogFilter.class);

    private String usernameKey = "username";
    private Integer payloadMaxLength = 1024;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        Long startTime = System.currentTimeMillis();
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

        filterChain.doFilter(requestWrapper, responseWrapper);

        String requestPayload = getPayLoad(requestWrapper.getContentAsByteArray(),
                request.getCharacterEncoding());
        String responsePayload = getPayLoad(responseWrapper.getContentAsByteArray(),
                response.getCharacterEncoding());
        responseWrapper.copyBodyToResponse();
        BLog.accessJsonLogBuilder()
            .addRequestPayLoad(requestPayload)
            .addResponsePayLoad(responsePayload)
            .put(request, usernameKey)
            .put(response)
            .put("_COST_", System.currentTimeMillis() - startTime)
            .log();
    }

    private String getPayLoad(byte[] buf, String characterEncoding) {
        String payload = "";
        if (buf == null) return payload;
        if (buf.length > 0) {
            int length = Math.min(buf.length, getPayloadMaxLength());
            try {
                payload = new String(buf, 0, length, characterEncoding);
            } catch (UnsupportedEncodingException ex) {
                payload = "[unknown]";
            }
        }
        return payload;
    }


    public String getUsernameKey() {
        return usernameKey;
    }

    public void setUsernameKey(String usernameKey) {
        this.usernameKey = usernameKey;
    }

    public Integer getPayloadMaxLength() {
        return payloadMaxLength;
    }

    public void setPayloadMaxLength(Integer payloadMaxLength) {
        this.payloadMaxLength = payloadMaxLength;
    }
}

其核心思想是實現了OncePerRequestFilter這個虛基類,而這個虛基類是實現了Filter接口,并且要基于SpringMVC框架的,因此這個方法只能適用于SpringMVC工程

核心方法是doFilterInternal

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                FilterChain filterChain) throws ServletException, IOException {

    Long startTime = System.currentTimeMillis();
    ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
    ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

    filterChain.doFilter(requestWrapper, responseWrapper);

    String requestPayload = getPayLoad(requestWrapper.getContentAsByteArray(),
            request.getCharacterEncoding());
    String responsePayload = getPayLoad(responseWrapper.getContentAsByteArray(),
            response.getCharacterEncoding());
    responseWrapper.copyBodyToResponse();
    BLog.accessJsonLogBuilder()
        .addRequestPayLoad(requestPayload)
        .addResponsePayLoad(responsePayload)
        .put(request, usernameKey)
        .put(response)
        .put("_COST_", System.currentTimeMillis() - startTime)
        .log();
}

首先通過

ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);

這兩個方式對請求和返回進行包裝(讓body可以被緩存),這樣來解決inputstream不能多次讀取的問題

調用

filterChain.doFilter(requestWrapper, responseWrapper);

來執行其他的filter之后

可以通過

    String requestPayload = getPayLoad(requestWrapper.getContentAsByteArray(),
            request.getCharacterEncoding());
    String responsePayload = getPayLoad(responseWrapper.getContentAsByteArray(),
            response.getCharacterEncoding());

這種方式取出request和response的payload,不要忘記

    responseWrapper.copyBodyToResponse();

重新寫入response

最后打印出整個log

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容