스프링 부트

[SpringBoot] 필터를 이용하여 요청 로그 남기기! (CommonsRequestLoggingFilter)

h__hj 2022. 10. 22. 23:49

# [SpringBoot] CommonsRequestLoggingFilter example code

 스프링 부트의 CommonsRequestLoggingFilter 를 이용하여 client, header, querystring, payload 로그 남기기

# 환경

tool  : STS 4.13.0
ver   : 2.7.3-SNAPSHOT
java  : 11
repo  : MAVEN
view  : THYMELEAF
jQuery: 3.6.0

# Filter

public class CommonLoggingFilter extends CommonsRequestLoggingFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException 
    {
        if(FilterConfig.isStatic(request.getRequestURI())) {
            filterChain.doFilter(request, response);
        } else {
            super.doFilterInternal(request, response, filterChain);
        }
    }
    
    // 오버라이드 안해도 됨.
    @Override
    protected void beforeRequest(HttpServletRequest request, String message) {
        super.beforeRequest(request, message);
    }
    // 오버라이드 안해도 됨.
    @Override
    protected void afterRequest(HttpServletRequest request, String message) {
        super.afterRequest(request, message);
    }
}

# Configuration

@Configuration
public class FilterConfig {
	
    public static boolean isStatic(String uri) {
        if(uri.startsWith("/js") || uri.startsWith("/css") || uri.startsWith("/images") || uri.startsWith("/favicon.ico") || uri.startsWith("/jwt")) {
            return true;
        } 
        return false;
    }

    @Bean
    public FilterRegistrationBean<CommonLoggingFilter> loggingFilter() {
        CommonLoggingFilter commonLoggingFilter = new CommonLoggingFilter();
        commonLoggingFilter.setIncludeHeaders(true);        // header info
        commonLoggingFilter.setIncludeClientInfo(true);     // client info
        commonLoggingFilter.setIncludeQueryString(true);    // QUERYSTRING: only GET
        commonLoggingFilter.setIncludePayload(true);        // PAYLOAD: only after and only POST 
        commonLoggingFilter.setMaxPayloadLength(1000);
        
        commonLoggingFilter.setBeforeMessagePrefix("[LOG] [BEFORE] [");
        commonLoggingFilter.setBeforeMessageSuffix("]");
        
        commonLoggingFilter.setAfterMessagePrefix("[LOG] [AFTER] [");
        commonLoggingFilter.setAfterMessageSuffix("]");
        
        FilterRegistrationBean<CommonLoggingFilter> bean = new FilterRegistrationBean<>(commonLoggingFilter);
        bean.addUrlPatterns("/*");
        bean.setOrder(2);
        bean.setEnabled(true);
        return bean;
    }
}

# LOG

// GET 요청 시! (HttpInterceptor - preHandle 이전)
[LOG] [BEFORE] [GET /test/send/get1?param1=dfbghdfh&param2=get1, client=0:0:0:0:0:0:0:1, session=6C8F27CAD3977E64AC159579789A9E97, headers=[host:"localhost:8888", connection:"keep-alive", sec-ch-ua:""Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"", accept:"application/json, text/javascript, */*; q=0.01", x-requested-with:"XMLHttpRequest", sec-ch-ua-mobile:"?0", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36", header-name:"header-value", sec-ch-ua-platform:""Windows"", sec-fetch-site:"same-origin", sec-fetch-mode:"cors", sec-fetch-dest:"empty", referer:"http://localhost:8888/test/send/view", accept-encoding:"gzip, deflate, br", accept-language:"ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"XSRF-TOKEN=d03e945f-2bc8-4182-8e56-3d8e72d460a1; JSESSIONID=6C8F27CAD3977E64AC159579789A9E97"]]

// GET 요청 완료 시! (HttpInterceptor - afterCompletion 이후)
[LOG] [AFTER] [GET /test/send/get1?param1=dfbghdfh&param2=get1, client=0:0:0:0:0:0:0:1, session=6C8F27CAD3977E64AC159579789A9E97, headers=[host:"localhost:8888", connection:"keep-alive", sec-ch-ua:""Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"", accept:"application/json, text/javascript, */*; q=0.01", x-requested-with:"XMLHttpRequest", sec-ch-ua-mobile:"?0", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36", header-name:"header-value", sec-ch-ua-platform:""Windows"", sec-fetch-site:"same-origin", sec-fetch-mode:"cors", sec-fetch-dest:"empty", referer:"http://localhost:8888/test/send/view", accept-encoding:"gzip, deflate, br", accept-language:"ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"XSRF-TOKEN=d03e945f-2bc8-4182-8e56-3d8e72d460a1; JSESSIONID=6C8F27CAD3977E64AC159579789A9E97"]]

// POST 요청 시! (HttpInterceptor - preHandle 이전)
[LOG] [BEFORE] [POST /test/send/post8, client=0:0:0:0:0:0:0:1, session=6C8F27CAD3977E64AC159579789A9E97, headers=[host:"localhost:8888", connection:"keep-alive", content-length:"43", sec-ch-ua:""Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"", x-xsrf-token:"d03e945f-2bc8-4182-8e56-3d8e72d460a1", sec-ch-ua-mobile:"?0", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36", content-type:"application/x-www-form-urlencoded; charset=UTF-8", accept:"application/json, text/javascript, */*; q=0.01", x-requested-with:"XMLHttpRequest", header-name:"header-value", sec-ch-ua-platform:""Windows"", origin:"http://localhost:8888", sec-fetch-site:"same-origin", sec-fetch-mode:"cors", sec-fetch-dest:"empty", referer:"http://localhost:8888/test/send/view", accept-encoding:"gzip, deflate, br", accept-language:"ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"XSRF-TOKEN=d03e945f-2bc8-4182-8e56-3d8e72d460a1; JSESSIONID=6C8F27CAD3977E64AC159579789A9E97"]]

// POST 요청 완료 시! (HttpInterceptor - afterCompletion 이후)
[LOG] [AFTER] [POST /test/send/post8, client=0:0:0:0:0:0:0:1, session=6C8F27CAD3977E64AC159579789A9E97, headers=[host:"localhost:8888", connection:"keep-alive", content-length:"43", sec-ch-ua:""Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"", x-xsrf-token:"d03e945f-2bc8-4182-8e56-3d8e72d460a1", sec-ch-ua-mobile:"?0", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36", content-type:"application/x-www-form-urlencoded; charset=UTF-8", accept:"application/json, text/javascript, */*; q=0.01", x-requested-with:"XMLHttpRequest", header-name:"header-value", sec-ch-ua-platform:""Windows"", origin:"http://localhost:8888", sec-fetch-site:"same-origin", sec-fetch-mode:"cors", sec-fetch-dest:"empty", referer:"http://localhost:8888/test/send/view", accept-encoding:"gzip, deflate, br", accept-language:"ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7", cookie:"XSRF-TOKEN=d03e945f-2bc8-4182-8e56-3d8e72d460a1; JSESSIONID=6C8F27CAD3977E64AC159579789A9E97"], payload=param%5B%5D=dfbghdfhA&param%5B%5D=dfbghdfhB]

# 내용

  • CommonsRequestLoggingFilter를 상속받은 Class 작성 후 FilterConfig에 설정!
  • include 설정을 이용하여, 로그를 남기고자 하는 정보를 On/Off 할 수 있음.
  • querystring은 GET 방식 일 때, url에 같이 나타난다.
  • payload는 본문을 사용하는 방식 일 때, after에만 나타난다.
  • MessagePrefix/Suffix 설정을 이용하여, 로그 앞과 뒤에 남기고자 하는 정보를 남길 수 있다.
    • before: [LOG] [BEFORE] [, ]
    • after: [LOG] [AFTER] [, ]
  • 상속받은 Filter 파일에서 beforeRequest, afterRequest를 오버라이드하고 사용하고 싶지 않은부분을 주석처리하면 로그를 안남길 수 있다.
    • // super.afterRequest(request, message);
    • 하면 after 부분 로그X
    • 오버라이드 안해도 로그는 나옴.
  • 근데 나는,,, client 정보만 받고, before만 사용 할 듯,,,(HttpInterceptor에서 header, cookie, session 로그 남김)