Log Incoming Requests

2 min read

http://www.baeldung.com/spring-http-logging

HTTP 요청을 로그로 출력할 때에는 HandlerInterceptorAdapter를 상속받는 클래스를 정의하면 preHandle 메소드에서 HttpServletRequest의 내용을 출력할 수 있다. 이 때 HttpServletRequest의 특성상 inputStream 을 사용하게 되면 다음에 이 서블릿 리퀘스트를 읽는 부분에서 에러가 발생한다. 이를 해결하기 위해 Spring에서 제공하는 ContentCachingRequestWrapperContentCachingResponseWrapper를 사용할 수 있다.

HttpServletRequest requestCacheWrapperObject
  = new ContentCachingRequestWrapper(request);
requestCacheWrapperObject.getParameterMap();
// Read inputStream from requestCacheWrapperObject and log it

이 때 두가지 한계점이 있다.

  • 아래와 같은 요청에서만 사용할 수 있다.

    Content-Type:application/x-www-form-urlencoded Method-Type:POST

  • 사용하기 전에 getParameterMap()을 반드시 호출해야 한다.

Spring이 제공하는 CommonsRequestLoggingFilter를 사용하면 이 문제를 해결할 수 있다.

CommonsRequestLoggingFilter

스프링 부트를 사용할 경우 CommonsRequestLoggingFitler를 빈으로 등록하고, 로그 레벨을 지정하는 것으로 바로 사용이 가능하다.

@Configuration
public class RequestLoggingFilterConfig {
    @Bean
    public CommonsRequestLoggingFilter logFilter() {
        CommonsRequestLoggingFilter filter
          = new CommonsRequestLoggingFilter();
        filter.setIncludeQueryString(true);
        filter.setIncludePayload(true);
        filter.setMaxPayloadLength(10000);
        filter.setIncludeHeaders(false);
        filter.setAfterMessagePrefix("REQUEST DATA : ");
        return filter;
    }
}

logback-spring.xml파일에서 로그 레벨을 DEBUG로 설정한다. 스프링 부트에서 로그백 사용하기 참고.

<logger name="org.springframework.web.filter.CommonsRequestLoggingFilter">
    <level value="DEBUG" />
</logger>

application.properties의 경우 다음과 같이 설정한다.

logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter= DEBUG

CommonsRequestLoggingFitler를 사용할 때 POST요청의 payload는 response after 구문에서 출력이 된다.

Spring Boot Actuator Trace

로그를 직접 출력하는 대신 actuator를 사용하는 방법도 있다. build.gradle에 아래 내용을 추가한다.

dependencies {
  compile('org.springframework.boot:spring-boot-starter-actuator')
  ...
}

application.yml파일에 아래 내용을 추가한다.

management:
  context-path: /manage             # URL 정의
  security:
    enabled: false                  # 인증 없이 사용
  trace:
    include: parameters, errors     # 표시할 정보 선택

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss # 타임스탬프 읽기 좋게 변경

부트 앱을 실행하고 /manage/trace로 접속하면 최근 요청을 볼 수 있다. 이 때 POST요청의 payload는 출력되지 않는다.

WebRequestTraceFilter

WebRequestTraceFilter를 사용하면 /trace에서 출력되는 내용을 변경할 수 있다. POST요청의 payload가 출력되지 않는 문제도 해결 가능하다.

@Component
public class TraceFilter extends WebRequestTraceFilter {

    public TraceFilter(TraceRepository repository, TraceProperties properties) {
        super(repository, properties);
    }

    @Override
    protected Map<String, Object> getTrace(HttpServletRequest request) {
        Map<String, Object> trace = super.getTrace(request);
        trace.put("requestBody", getPostBody(request));
        return trace;
    }
}

위와 같이 getTrace부분에서 출력하고 싶은 내용을 추가하면 된다. getPostBody 구현은 생략한다. HttpServletRequestWrapperServletInputStream을 상속받는 클래스를 구현해서 HttpServletRequest의 inputStream을 복사한 뒤 사용하도록 구현해야 한다. (복잡하다) 구글 검색 결과를 참조해서 구현할 수 있다.

Controller

POST요청의 payload를 꼭 출력하고 싶다면 요청을 처리하는 Controller의 메소드 안에서 변환된 객체를 출력하는게 좋을 것 같다.

© 2023 Raegon Kim