-
[Spring Boot] request, response 로그 남기기1 - Console: 트러블슈팅개발기록 2022. 10. 11. 14:41반응형728x90
문제 발생
api 중 파라미터를 필요로 하는 api에서 로깅을 할 경우 파라미터가 사라지는 이슈가 발생했다.
알고보니 httpServeltRequest를 읽는 것(getInputStream)은 일회성만 가능하다고 한다.
참고: https://leeyongjin.tistory.com/entry/request-response-logging
cf) 이전 포스트: https://jeongwoo.tistory.com/38
이전 포스트에서는 파라미터가 필요없는 api에 대해 request에 파라미터를 추가해서 날리고 테스트 했기 때문에 상관이 없었다.
하지만 실무에서 해당 이슈때문에 급하게 미봉책으로 수정한 코드를 기록으로 남긴다.
+ 다음에 시간이 되면 참고한 블로그를 따라해보든 해서 온전한 해결책을 찾아봐야겠다.
해결책
Spring-web에서 이미 구현해둔 ContentCachingRequestWrapper를 사용했다.
최종 코드
1. build.gradle
// HttpServletRequestWrapper api 'org.springframework.boot:spring-boot-starter-web'
2. CachingResponseWrapper.java
cf) CachingRequestWrapper.java의 경우 ContentCachingRequestWrapper를 사용했기 때문에 쓰레기 코드가 되어 삭제했다.
package com.??.filter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.output.TeeOutputStream; import org.springframework.util.FastByteArrayOutputStream; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.*; @Slf4j public class CachingResponseWrapper extends HttpServletResponseWrapper { private final FastByteArrayOutputStream content = new FastByteArrayOutputStream(1024); private ServletOutputStream outputStream; public CachingResponseWrapper(HttpServletResponse response) { super(response); } @Override public ServletOutputStream getOutputStream() throws IOException { if (this.outputStream == null) { this.outputStream = new CachingResponseWrapper.CachedServletOutputStream(getResponse().getOutputStream(), this.content); } return this.outputStream; } public InputStream getContentInputStream() { return this.content.getInputStream(); } private class CachedServletOutputStream extends ServletOutputStream { private final TeeOutputStream targetStream; public CachedServletOutputStream(OutputStream one, OutputStream two) { targetStream = new TeeOutputStream(one, two); } @Override public void write(int arg) throws IOException { this.targetStream.write(arg); } @Override public void write(byte[] buf, int off, int len) throws IOException { this.targetStream.write(buf, off, len); } @Override public void flush() throws IOException { super.flush(); this.targetStream.flush(); } @Override public void close() throws IOException { super.close(); this.targetStream.close(); } @Override public boolean isReady() { return false; } @Override public void setWriteListener(WriteListener writeListener) { throw new UnsupportedOperationException("not support"); } } }
3. RequestResponseWrapperFilter.java
package com.??.filter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Slf4j @Component public class RequestResponseWrapperFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper wrappingRequest = new ContentCachingRequestWrapper(request); if (isAsyncDispatch(request)) { filterChain.doFilter(request, response); } else { filterChain.doFilter(wrappingRequest, new CachingResponseWrapper(response)); } } }
4. HttpLogInterceptor.java
package com.??.interceptor; import com.fasterxml.jackson.databind.ObjectMapper; import com.??.filter.CachingResponseWrapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.io.StringWriter; import java.net.URLDecoder; @Slf4j @Component public class HttpLogInterceptor implements HandlerInterceptor { private final ObjectMapper objectMapper; public HttpLogInterceptor(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request instanceof ContentCachingRequestWrapper) { log.info("uri: " + request.getRequestURI()); } return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { InputStream tmpIs = ((CachingResponseWrapper) response).getContentInputStream(); StringWriter writer = new StringWriter(); IOUtils.copy(tmpIs, writer, "UTF-8"); String res = writer.toString(); log.info("response - {}", res); } }
5. WebMvcConfig.java
@Autowired로 HttpLogInterceptor httpLongInterceptor를 DI했던 것을 final로 리팩토링했다.
package com.??.config; import com.??.interceptor.HttpLogInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @ComponentScan(basePackages={"com.??.interceptor"}) public class WebMvcConfig implements WebMvcConfigurer { private final HttpLogInterceptor httpLogInterceptor; public WebMvcConfig(HttpLogInterceptor httpLogInterceptor) { this.httpLogInterceptor = httpLogInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(httpLogInterceptor) .addPathPatterns("/**"); } }
728x90'개발기록' 카테고리의 다른 글
[Spring Boot] boolean값이 0 또는 false만 return할 때 해결 방법 (0) 2022.11.02 [Spring Boot] Real Remote(Client) IP Address 얻기 (1) 2022.10.11 [메모장 hosts] hosts파일에서 ip와 도메인 주소 매핑하기 (0) 2022.09.27 [Spring Boot] Mybatis 설정(In application.properties) (0) 2022.09.27 [Error, Spring Boot + JPA] repository 위치 변경 시 에러 (0) 2022.09.22