반응형

[Spring Boot 실습 #8] Access Log - request body 확인

 

1.HttpServletRequest의 InputStream을 ByteArray에 저장하여 데이터를 재사용

2. 개발환경

    2.1. 개발환경

    2.2. 라이브러리

3. 구현

    3.1. 프로젝트 구조

    3.2. 구현 - 소스코드

4. 결과

    4.1. GET

    4.2. GET + queryString

    4.3. POST + Request Body

 

 

1. HttpServletRequest의 InputStream을 ByteArray에 저장하여 데이터를 재사용

저장한 ByteArray를 InputStream으로 받을 수 있는 ServletInputStream을 상속 받은 class를 생성하여 데이터를 재사용

request body로그를 web transaction 순서에 맞게 찍기

 

참고

[Java 실습 #18] InputStream 재사용 (3) - InputStream 재사용 class 활용

 

[Java 실습 #18] InputStream 재사용 (3) - InputStream 재사용 class 활용

[Java 실습 #18] InputStream 재사용(3) - InputStream 재사용 class 활용 1. InputStream 재사용 class 생성하여 호출 - ByteArray에 저장하여 데이터를 재사용 2. 개발환경 2.1. 개발환경 2.2. 라이브..

tlo-developer.tistory.com

 

 

2. 개발환경

 

2.1. 개발환경

MacOS M1 - macOS Monterey 12.0.1

IntelliJ IDEA 2021.2 (Community Edition)

 

2.2. 라이브러리

JDK 11

spring-boot-2.6.4

 

 

3. 구현

3.1. 프로젝트 구조

 

3.2. 구현 - 소스코드

 

[ pom.xml ]

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>io.home.test</groupId>
    <artifactId>spring-boot-accesslog</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-accesslog</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
</project>

 

[ SpringBootAccessLogApplication.java ]

1
2
3
4
5
6
7
8
9
10
11
12
13
package io.home.test;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class SpringBootAccessLogApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringBootAccessLogApplication.class, args);
    }
 
}

 

[ DefaultController.java ]

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
package io.home.test.base.web;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
 
@RestController
@RequestMapping(value = "/api/access")
public class DefaultController {
    Logger LOG = LoggerFactory.getLogger(DefaultController.class);
 
    @GetMapping
    public ResponseEntity<String> defaultGetResource() {
        LOG.info("---- access controller GET");
        return ResponseEntity.ok("GET Hello World!");
    }
 
    @GetMapping(value = "/params")
    public ResponseEntity<String> defaultGetParamsResource(@RequestParam(value = "queryString"String queryString) {
        LOG.info("---- access controller GET + Params?" + queryString);
        return ResponseEntity.ok("GET + Params Hello World!");
    }
 
    @PostMapping
    public ResponseEntity<String> defaultPostResource(@RequestBody String body) {
        LOG.info("---- access controller POST\n" + "body: " + body);
        return ResponseEntity.ok("POST Hello World!\n" + "body: " + body);
    }
}

 

[ AccessLogFilter.java ]

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
112
113
114
115
116
117
118
119
120
121
122
123
package io.home.test.base.filter;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
 
@Component
public class AccessLogFilter implements Filter {
 
    private static final Logger LOG = LoggerFactory.getLogger(AccessLogFilter.class);
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
 
    @Override
    public void destroy() {
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        CustomRequestWrapper customRequestWrapper = new CustomRequestWrapper((HttpServletRequest) servletRequest);
        String accessLog = buildAccessLog(customRequestWrapper);
        LOG.info(accessLog);
 
        try {
            filterChain.doFilter(customRequestWrapper, servletResponse);
        } finally {
            customRequestWrapper.setBody(null);
        }
    }
 
    private String buildAccessLog(CustomRequestWrapper customRequestWrapper) {
 
        String requestURL = getRequestURL(customRequestWrapper);
        String remoteAddr = getRemoteAddr(customRequestWrapper);
        String method = getMethod(customRequestWrapper);
        String queryString = getQueryString(customRequestWrapper);
        String requestBody = getRequestBody(customRequestWrapper);
 
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        if (requestURL != null) {
            sb
                    .append("\"").append("requestURL").append("\"")
                    .append(":")
                    .append("\"").append(requestURL).append("\"");
        }
        if (remoteAddr != null) {
            sb
                    .append(",")
                    .append("\"").append("remoteAddr").append("\"")
                    .append(":")
                    .append("\"").append(remoteAddr).append("\"");
        }
        if (method != null) {
            sb
                    .append(",")
                    .append("\"").append("method").append("\"")
                    .append(":")
                    .append("\"").append(method).append("\"");
        }
        if (queryString != null) {
            sb
                    .append(",")
                    .append("\"").append("queryString").append("\"")
                    .append(":")
                    .append("\"").append(queryString).append("\"");
        }
        if (requestBody != null && requestBody.length() > 0) {
            sb
                    .append(",")
                    .append("\"").append("body").append("\"")
                    .append(":")
                    .append("\"").append(requestBody).append("\"");
        }
        sb.append("}");
        return sb.toString();
    }
 
    private String getRequestBody(CustomRequestWrapper customRequestWrapper) {
        String content = null;
        String method = customRequestWrapper.getMethod().toLowerCase();
 
        // POST, PUT + application/json
        if (method.startsWith("p")) {
            if (customRequestWrapper.getContentType().toLowerCase().indexOf("json"> 0) {
                try {
                    content = new String(customRequestWrapper.getBody(), customRequestWrapper.getCharacterEncoding());
                } catch (UnsupportedEncodingException e) {
                    LOG.error(e.getMessage());
                }
            }
        }
        return content;
    }
 
    private String getQueryString(CustomRequestWrapper customRequestWrapper) {
        String queryString = null;
        if (customRequestWrapper.getQueryString() != null) {
            queryString = customRequestWrapper.getQueryString();
        }
        return queryString;
    }
 
    private String getMethod(CustomRequestWrapper customRequestWrapper) {
        return customRequestWrapper.getMethod();
    }
 
    private String getRemoteAddr(CustomRequestWrapper customRequestWrapper) {
        return customRequestWrapper.getRemoteAddr();
    }
 
    private String getRequestURL(CustomRequestWrapper customRequestWrapper) {
        return customRequestWrapper.getRequestURL().toString();
    }
 
}

 

[ CachedByteArrayInputStream.java ]

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
package io.home.test.base.filter;
 
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
 
public class CachedByteArrayInputStream extends ServletInputStream {
    private ByteArrayInputStream in;
 
    public CachedByteArrayInputStream(byte[] body) {
        this.in = new ByteArrayInputStream(body);
    }
 
    @Override
    public boolean isFinished() {
        return false;
    }
 
    @Override
    public boolean isReady() {
        return false;
    }
 
    @Override
    public void setReadListener(ReadListener readListener) {
    }
 
    @Override
    public int read() {
        return in.read();
    }
}

 

[ CustomRequestWrapper.java ]

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
package io.home.test.base.filter;
 
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
 
public class CustomRequestWrapper extends HttpServletRequestWrapper {
 
    private byte[] body;
 
    public CustomRequestWrapper(HttpServletRequest httpServletRequest) {
        super(httpServletRequest);
        try {
//                this.body = IOUtils.toByteArray(httpServletRequest.getInputStream());
            DataInputStream dis = new DataInputStream(httpServletRequest.getInputStream());
            ByteArrayOutputStream os = new ByteArrayOutputStream();
//                byte[] buffer = new byte[0xFFFF];
            byte[] buffer = new byte[1024];
            for (int len = dis.read(buffer); len != -1; len = dis.read(buffer)) {
                os.write(buffer, 0, len);
            }
            os.flush();
            this.body = os.toByteArray();
        } catch (IOException ioe) {
            System.out.println("IOException");
        }
    }
 
    public byte[] getBody() {
        return body;
    }
 
    public void setBody(byte[] body) {
        this.body = body;
    }
 
    @Override
    public ServletInputStream getInputStream() {
        return new CachedByteArrayInputStream(this.body);
    }
 
}

 

 

4. 결과

4.1. GET

GET  http://localhost:8080/api/access

 

4.2. GET + queryString

GET  http://localhost:8080/api/access/params?querString=abcd

 

4.3. POST + Request Body

Postman 활용하여 POST 호출 

POST  http://localhost:8080/api/access

{
    "id": "dd1bcddd",
    "title": "test-access",
    "content": "testing access log"
}
 
 

 

 

반응형