-
Spring Filter Logging- Spring Filter Hands On 4Spring Framework/Filter 2023. 10. 15. 00:01
개요
Spring Filter를 활용해서 Response, Request에 대한 로깅을 수행해보고자 합니다.
시작 전 주의사항
HttpServletRequest, HttpServletResponse의 경우에 내부적으로 Stream을 사용하기 때문에 ContentCachingRequestWrapper와 ContentCachingResponseWrapper를 사용해주어야 합니다.
Stream은 한번 소비하면 재사용할 수 없습니다.
Response Request Logging
class ResponseRequestLoggingFilter : OncePerRequestFilter() { override fun doFilterInternal( request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain ) { val requestWrapper = ContentCachingRequestWrapper(request) val responseWrapper = ContentCachingResponseWrapper(response) val start = System.currentTimeMillis() filterChain.doFilter(requestWrapper, responseWrapper) val end = System.currentTimeMillis() com.example.study.log.logger.info{ """ "[REQUEST] ${request.method} - ${request.requestURI} ${responseWrapper.status} - ${(end - start) / 1000.0} "Headers : ${getHeaders(request)} "Request : ${getRequestBody(requestWrapper)} "Response : ${getResponseBody(responseWrapper)} """.trimIndent() } } private fun getHeaders(request: HttpServletRequest): Map<Any, Any> { val headerMap: MutableMap<Any, Any> = HashMap<Any, Any>() val headerArray: Enumeration<*> = request.headerNames while (headerArray.hasMoreElements()) { val headerName = headerArray.nextElement() as String headerMap[headerName] = request.getHeader(headerName) } return headerMap } private fun getRequestBody(request: ContentCachingRequestWrapper): String { val wrapper = WebUtils.getNativeRequest( request, ContentCachingRequestWrapper::class.java ) if (wrapper != null) { val buf = wrapper.contentAsByteArray if (buf.size > 0) { try { return String(buf, 0, buf.size, charset(wrapper.characterEncoding)) } catch (e: UnsupportedEncodingException) { return " - " } } } return " - " } @Throws(IOException::class) private fun getResponseBody(response: HttpServletResponse): String { var payload: String? = null val wrapper = WebUtils.getNativeResponse( response, ContentCachingResponseWrapper::class.java ) if (wrapper != null) { val buf = wrapper.contentAsByteArray if (buf.size > 0) { payload = String(buf, 0, buf.size, charset(wrapper.characterEncoding)) wrapper.copyBodyToResponse() } } return payload ?: " - " } }
여러 가지 Controller를 구현하여 테스트
@GetMapping("/path-and-header-logging/{user-id}") fun pathVariableAndHeaderLogging(@RequestHeader role: String, @PathVariable("user-id") id: String): ResponseEntity<TestDTO> { return ResponseEntity.ok( TestDTO( role = role, id = id, ) ) } ### 로깅 테스트 GET localhost:8080/path-and-header-logging/1234 role: admin
@PostMapping("/request-body") fun responseEntity(@RequestBody testDTO: TestDTO): String{ println(testDTO) return "ok" } ### 로깅 테스트 POST localhost:8080/request-body content-type: application/json role: admin { "id": "1234", "role": "user" }
@GetMapping("/exception-log") fun illegalArgumentExceptionLogging() { throw IllegalArgumentException("컨트롤러로 들어와서 예외발생") } ### 로깅 예외 테스트 GET localhost:8080/exception-log role: admin
마무리
@RestControllerAdvice class GlobalExceptionHandler { @ExceptionHandler(Exception::class) fun globalException(e: Exception): ResponseEntity<String> { return ResponseEntity .status(HttpStatus.BAD_REQUEST) .body(e.message) } }
위의 방법으로 구현하면 FilterChain 과정에서 예외가 발생하는 경우에는 Filter Logging이 남지 않습니다.
이런 경우에는 try-catch나 runCatching으로 잡아서 해결해주어야 합니다.
또한 컨트롤러에 진입하여 예외가 발생하는 경우 ControllerAdvice에서 예외 메시지를 response Body로 전달해 주어야 합니다.
response에 예외 메시지가 출력됩니다.
'Spring Framework > Filter' 카테고리의 다른 글
Spring Filter Security로 인가처리 - Spring Filter Hands On 3 (0) 2023.10.06 Spring Filter Header 인가 처리 - Spring Filter Hands On 2 (1) 2023.10.05 Spring Filter란? - Spring Filter Hands On 1 (0) 2023.10.04