一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringMVC框架中使用Filter實現請求日志打印方式

SpringMVC框架中使用Filter實現請求日志打印方式

2022-03-07 00:37忙里偷閑得幾回 Java教程

這篇文章主要介紹了SpringMVC框架中使用Filter實現請求日志打印方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

之前利用HttpServletRequest.getInputStream()和RequestWrapper實現了請求的requestBody獲取,現在提出將一個請求的RequestBody和ResponseBody都提出來并打印日志&落入數據庫,以便統計和查找問題。

查找資料后確定兩種技術方案

1. 使用AOP對所有Controller的方法進行環繞通知處理;

2. 使用Filter攔截所有的Request和Response,并獲取body。

最后選擇了第二種方式,具體實現記錄如下。

具體實現

日志記錄過濾器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class RequestFilter implements Filter{
private static final String LOG_FORMATTER_IN = "請求路徑:{%s},請求方法:{%s},參數:{%s},來源IP:{%s},請求開始時間{%s},返回:{%s},請求結束時間{%s},用時:{%s}ms,操作類型:{%s},操作人:{%s}";
public static final String USER_TOKEN_REDIS_PREFIX = "token_prefix";
private static final Logger log = LoggerFactory.getLogger(RequestFilter.class);
//request攔截的conten-type列表
private List<String> contentTypes;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    //請求路徑
    String path = httpServletRequest.getRequestURI();
    String method = httpServletRequest.getMethod();
    //所有請求參數的Map
    Map<String,String> paramMap = new HashMap<>();
    //請求的真實IP
    String requestedIP = RequestUtils.getRealIP(httpServletRequest);
    //是否攔截并包裝請求,如果需要攔截則會獲取RequestBody,一般為application/json才攔截
    boolean filterRequestFlag = checkFilter(request.getContentType());
    if (filterRequestFlag) {
        httpServletRequest = new MyRequestBodyReaderWrapper(httpServletRequest);
    }
    //獲取所有queryString和requestBody
    Map<String, String> requestParamMap = RequestUtils.getRequestParamMap(httpServletRequest);
    if (requestParamMap != null && !requestParamMap.isEmpty()){
        paramMap.putAll(requestParamMap);
    }
    //獲取header參數
    Map<String, String> headerMap = RequestUtils.getHeaders(httpServletRequest);
    if (headerMap != null && !headerMap.isEmpty()){
       paramMap.putAll(headerMap);
    }
    //獲取路徑參數
    Map<String,String> uriTemplateMap = RequestUtils.getUriTemplateVar(httpServletRequest);
    if (uriTemplateMap != null && !uriTemplateMap.isEmpty()){
        paramMap.putAll(uriTemplateMap);
    }
    //包裝Response,重寫getOutputStream()和getWriter()方法,并用自定義的OutputStream和Writer來攔截和保存ResponseBody
    MyResponseWrapper responseWrapper = new MyResponseWrapper(httpServletResponse);
    //請求開始時間
    Long dateStart = System.currentTimeMillis();
    //Spring通過DispatchServlet處理請求
    chain.doFilter(httpServletRequest, responseWrapper);
    //請求結束時間
    Long dateEnd = System.currentTimeMillis();
    String responseBody;
    if (responseWrapper.getMyOutputStream() == null){
            if (responseWrapper.getMyWriter() != null){
                responseBody = responseWrapper.getMyWriter().getContent();
                //一定要flush,responseBody會被復用
                responseWrapper.getMyWriter().myFlush();
            }
        }else {
            responseBody = responseWrapper.getMyOutputStream().getBuffer();
            //一定要flush,responseBody會被復用
            responseWrapper.getMyOutputStream().myFlush();
    }
    String params = JSONObject.toJSONString(paramMap);
    log.info(String.format(LOG_FORMATTER_IN, path, method, params, requestedIP, dateStart, responseBody, dateEnd,(dateEnd - dateStart));
}
/**
 * 判斷請求/返回是否為application/json
 * 是則進行攔截,
 * 否則退出
 * @param contentType 請求/響應類型
 */
private boolean checkFilter(String contentType) {
    boolean filterFlag = false;//是否繼續攔截
    for (String p : getContentTypes()) {
        if (StringUtils.contains(contentType, p)){
            filterFlag = true;
        }
    }
    if (StringUtils.isEmpty(contentType)){
        filterFlag = true;
    }
    return filterFlag;
}
}

Request包裝器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
* HttpServletRequest的包裝器,為了在攔截器階段獲取requestBody且不妨礙SpringMVC再次獲取requestBody
*/
@Slf4j
public class MyRequestBodyReaderWrapper extends HttpServletRequestWrapper {
//存放JSON數據主體
private final byte[] body;
public MyRequestBodyReaderWrapper(HttpServletRequest request) throws IOException {
    super(request);
    body = getBody(request).getBytes(Charset.forName("UTF-8"));
}
@Override
public ServletInputStream getInputStream() throws IOException {
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
    return new ServletInputStream() {
        @Override
        public int read() throws IOException {
            return byteArrayInputStream.read();
        }
    };
}
@Override
public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
/**
 * 獲取請求Body
 */
public static String getBody(ServletRequest request) {
    StringBuilder sb = new StringBuilder();
    InputStream inputStream = null;
    BufferedReader reader = null;
    try {
        inputStream = request.getInputStream();
        reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
    } catch (IOException e) {
        log.error("MyRequestBodyReaderWrapper.getBody()異常-->",e);
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error("MyRequestBodyReaderWrapper.getBody()異常-->",e);
            }
        }
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                log.error("MyRequestBodyReaderWrapper.getBody()異常-->",e);
            }
        }
    }
    return sb.toString();
}
}

RequestUtils

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* 請求工具類
*/
public class RequestUtils {
private static final Logger logger = LoggerFactory.getLogger(RequestUtils.class);
/**
 * 獲取所有的請求頭
 * @param request
 * @return
 */
public static Map<String,String> getHeaders(HttpServletRequest request){
    Map<String,String> headerMap = new HashMap<>();
    List<String> headers = getCommonHeaders();
    headers.add("Postman-Token");
    headers.add("Proxy-Connection");
    headers.add("X-Lantern-Version");
    headers.add("Cookie");
    Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()){
        String headerName = headerNames.nextElement();
        if (headers.contains(headerName)){
            continue;
        }
        headerMap.put(headerName,request.getHeader(headerName));
    }
    return headerMap;
}
/**
 * 獲取請求的路徑參數
 * @param request
 * @return
 */
public static Map<String, String> getUriTemplateVar(HttpServletRequest request) {
    NativeWebRequest webRequest = new ServletWebRequest(request);
    Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
    return uriTemplateVars;
}
/**
 * 獲取請求的真實IP
 * @param request
 * @return
 */
public static String getRealIP(HttpServletRequest request) {
    String ip = request.getHeader("X-Forwarded-For");
    if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
        //多次反向代理后會有多個ip值,第一個ip才是真實ip
        int index = ip.indexOf(",");
        if (index != -1) {
            return ip.substring(0, index);
        } else {
            return ip;
        }
    }
    ip = request.getHeader("X-Real-IP");
    if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
        return ip;
    }
    return request.getRemoteAddr();
}
/**
 * 從Request中獲取所有的請求參數,包括GET/POST/PATCH等請求,不包括路徑參數
 * @param request
 * @return
 */
public static Map<String,String> getRequestParamMap(HttpServletRequest request) {
    Map<String,String> paramMap = new HashMap<>();
    //獲取QueryString中的參數,GET方式 或application/x-www-form-urlencoded
    Map<String, String> queryParamMap = RequestUtils.getUriQueryVar(request);
    if (queryParamMap != null){
        paramMap.putAll(queryParamMap);
    }
    //獲取Body中的參數,POST/PATCH等方式,application/json
    Map<String,String> bodyParamMap = null;
    try {
        //當為POST請求且 application/json時,request被RequestFilter處理為wrapper類
        if (!(request instanceof MyRequestBodyReaderWrapper)){
            return paramMap;
        }
        MyRequestBodyReaderWrapper readerWrapper = (MyRequestBodyReaderWrapper) request;
        String requestBody = new String(readerWrapper.getBody(), "UTF-8");
        if (com.zhongan.health.common.utils.StringUtils.isNotBlank(requestBody)){
            /**
             * 該方法為了避免 fastJson在 反序列化多層json時,改變對象順序
             */
            bodyParamMap = JSONObject.parseObject(requestBody, new TypeReference<LinkedHashMap<String,String>>(){}, Feature.OrderedField);
        }
    } catch (Exception e) {
        logger.error("獲取請求Body異常-->",e);
    }
    if (bodyParamMap != null){
        paramMap.putAll(bodyParamMap);
    }
    return paramMap;
}
private static List<String> getCommonHeaders(){
    List<String> headers = new ArrayList<>();
    Class<HttpHeaders> clazz = HttpHeaders.class;
    Field[] fields = clazz.getFields();
    for (Field field : fields) {
        field.setAccessible(true);
        if (field.getType().toString().endsWith("java.lang.String") && Modifier.isStatic(field.getModifiers())){
            try {
                headers.add((String) field.get(HttpHeaders.class));
            } catch (IllegalAccessException e) {
                logger.error("反射獲取屬性值異常-->",e);
            }
        }
    }
    return headers;
}
}

Response包裝器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
*該包裝器主要是重寫getOutputStream()和getWriter()方法,給調用者返回自定義的OutputStream和Writer,以便參與輸出的過程并記錄保存responseBody。
*/
public class MyResponseWrapper extends HttpServletResponseWrapper {
private ResponsePrintWriter writer;
private MyServletOutputStream out;
public MyResponseWrapper(HttpServletResponse response) {
    super(response);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
    //一定要先判斷當前out為空才能去新建out對象,否則一次請求會出現多個out對象
    if (out == null){
        out = new MyServletOutputStream(super.getOutputStream());
    }
    return out;
}
@Override
public PrintWriter getWriter() throws IOException {
    //一定要先判斷當前writer為空才能去新建writer對象,否則一次請求會出現多個writer對象
    if (writer == null){
        writer = new ResponsePrintWriter(super.getWriter());
    }
    return writer;
}
public ResponsePrintWriter getMyWriter() {
    return writer;
}
public MyServletOutputStream getMyOutputStream(){
    return out;
}
}

自定義Writer

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
*自定義Writer,重寫write方法,并記錄保存ResponseBody
*/
public class ResponsePrintWriter extends PrintWriter{
private StringBuffer buffer;
public ResponsePrintWriter(PrintWriter out) {
    super(out);
    buffer = new StringBuffer();
}
public String getContent(){
    return buffer == null ? null : buffer.toString();
}
@Override
public void flush() {
    super.flush();
}
//清空buffer,以便下一次重新使用
public void myFlush(){
    buffer = null;
}
@Override
public void write(char[] buf, int off, int len) {
    super.write(buf, off, len);
    char[] destination = new char[len];
    System.arraycopy(buf,off,destination,0,len);
    buffer.append(destination);
}
@Override
public void write(String s) {
    super.write(s);
    buffer.append(s);
}
}

自定義OutputStream

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 自定義輸出流包裝器,重寫write方法,并記錄保存ResponseBody
*/
public class MyServletOutputStream extends ServletOutputStream {
private ServletOutputStream outputStream;
private StringBuffer buffer;
public MyServletOutputStream(ServletOutputStream outputStream) {
    this.outputStream = outputStream;
    buffer = new StringBuffer();
}
@Override
public void write(int b) throws IOException {
    outputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
    outputStream.write(b, off, len);
    byte[] bytes = new byte[len];
    System.arraycopy(b, off, bytes, 0, len);
    buffer.append(new String(bytes,"UTF-8"));
}
@Override
public void write(byte[] b) throws IOException {
    outputStream.write(b);
}
@Override
public void flush() throws IOException {
    super.flush();
}
//清空buffer,以便下一次重新使用
public void myFlush(){
    outputStream = null;
    buffer = null;
}
public String getBuffer() {
    if (buffer != null){
        return buffer.toString();
    }
    return null;
}
}

總結一下

  • Request.getInputStream一次請求中只能被調用一次;
  • Response.getOutputStream()無法獲取ResponseBody;
  • Response的輸出有兩種方式,都需要考慮到并重寫

getOutputStream().write()

getWrite().write()

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/panyongcsd/article/details/80990397

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲天堂男人天堂 | 色综合久久综合网欧美综合网 | boobsmilking流奶水野战 | 91狠狠| 亚洲日本在线观看网址 | 男人天堂网址 | 调教女帝| 91麻豆在线观看 | 久久免费国产视频 | www.午夜剧场 | 俄罗斯一级成人毛片 | 99久久精彩视频 | 99热国产这里只有精品99 | 91在线亚洲综合在线 | 国产精品久久久久久福利 | 美女国内精品自产拍在线播放 | 日韩在线免费看 | 男人叼女人的痛爽视频免费 | 我与肥熟老妇的性事 | 双性人bbww欧美双性 | 挺进白嫩老师下面视频 | 国产亚洲一欧美一区二区三区 | 欧美人禽杂交在线视频 | 天天做日日做 | 亚洲四虎永久在线播放 | ady成人映画网站官网 | 亚拍一区 | 99精品国产美女福到在线不卡 | 啊啊啊好大在线观看 | 双性np玩烂了np欲之国的太子 | 三级黄色片在线观看 | 99视频在线观看视频一区 | 青青热久麻豆精品视频在线观看 | 无人区国产大片 | 亚洲精品一区波多野结衣 | 国产亚洲精品高清在线 | 亚洲干综合 | 秘书小说 | 国产亚洲精品一区二区在线播放 | 亚洲一区二区精品视频 | 日韩欧美不卡视频 |