HTTP 요청을 로그로 출력할 때에는 HandlerInterceptorAdapter
를 상속받는 클래스를 정의하면 preHandle
메소드에서 HttpServletRequest
의 내용을 출력할 수 있다. 이 때 HttpServletRequest
의 특성상 inputStream 을 사용하게 되면 다음에 이 서블릿 리퀘스트를 읽는 부분에서 에러가 발생한다. 이를 해결하기 위해 Spring에서 제공하는 ContentCachingRequestWrapper
와 ContentCachingResponseWrapper
를 사용할 수 있다.
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
를 사용하면 이 문제를 해결할 수 있다.
스프링 부트를 사용할 경우 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 구문에서 출력이 된다.
로그를 직접 출력하는 대신 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
를 사용하면 /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
구현은 생략한다. HttpServletRequestWrapper
와 ServletInputStream
을 상속받는 클래스를 구현해서 HttpServletRequest
의 inputStream을 복사한 뒤 사용하도록 구현해야 한다. (복잡하다) 구글 검색 결과를 참조해서 구현할 수 있다.
POST
요청의 payload를 꼭 출력하고 싶다면 요청을 처리하는 Controller의 메소드 안에서 변환된 객체를 출력하는게 좋을 것 같다.