스프링부트에서 로그 관리를 위해서는 크게 Filter, Interceptor,AOP 를 활용할 수 있다.
이중 Interceptor와 AOP는 스프링 내부에서 동작하는 반면, Filter는 스프링 외부에서 동작하여 스프링 내에서 가공되기 전 요청과, 가공된 후의 응답 값을 가져와 읽을 수 있다는 장점이 있어 이번에는 Filter를 활용하여 Request와 Response의 값을 로그로 출력해보려한다.
우선 로그를 출력하기 위해 아래 의존성을 추가해준다.
( lombok이 아닌 다른 의존성 혹은 방법을 사용해도 무관하다 )
implementation 'org.projectlombok:lombok'
당연하게도 로그를 띄우기 위해서는 외부에서 전달 받은 Request 값을 스프링으로 보내기 전 무조건 한 번은 읽어야 하는데, 이러한 서블릿 요청은 단 한 번만 읽을 수 있도록 설계가 되어 있어 Filter 에서 요청을 읽어 스프링으로 전달하면 오류가 발생하게 된다.
이러한 문제를 해결하기 위해서는 ContentCachingRequestWrapper 와 ContentCachingResponseWrapper 를 활용하면 되는데 이는 요청 값을 캐싱(복사) 하여 담아두어 요청을 여러번 읽을 수 있도록해 위와같은 문제를 해결할 수 있다.
@Slf4j
public class LogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
try {
filterChain.doFilter(requestWrapper, responseWrapper); // 요청을 필터에 전달
log.info("request : {}", new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8)); // request body값 출력
} finally {
log.info("response : {} ", new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8)); // response body값 출력
responseWrapper.copyBodyToResponse(); // 요청을 전달
}
}
}
이러한 Filter를 스프링 내에서 동작 시키기 위해서는 Bean 등록을 해주어야한다.
가장 간단한 방법은 해당 코드에 @Component 어노테이션을 추가해주는 방법이지만, 아래와 같이 Config 파일에 @Bean 어노테이션을 추가하여 사용해 관련 설정들을 하나의 파일에서 관리하도록 할 수도 있다.
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean logFilter() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
filterRegistrationBean.setFilter(new LogFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
두 방법 모두 장단점이 있기 때문에 목적에 맞게 선택하여 사용하면 될 것 같다.
'Log' 카테고리의 다른 글
로그 구분 ID 추가 logback-spring.xml + Filter (0) | 2024.08.12 |
---|---|
Filter 로그 처리 후 Controller에 요청 전달하기 (0) | 2024.08.08 |
Filter doFilter, ContentCachingWrapper, copyBodyToResponse 사용 이유 및 예시 (0) | 2024.08.06 |
ContentCachingRequestWrapper 동작 원리 (0) | 2024.08.04 |