포스트

2022-08-26-TIL

2022-08-26-TIL

Today I Learned

Spring Boot Logging

Spring Boot에서 요청 단위 로그를 남기는 위치는 Filter, Interceptor, AOP 세 곳 중 하나다. 각 위치가 실행되는 순서와 차이는 6월 29일 TIL에 정리했고, 여기서는 로깅 관점에서 무엇을 볼 수 있는지로 비교한다.

위치볼 수 있는 정보로깅에 적합한 내용
Filter서블릿 요청·응답 전체, 본문 스트림URL, 메서드, 상태 코드, 처리 시간, 요청·응답 본문
Interceptor요청과 매핑된 핸들러(컨트롤러 메서드)어떤 컨트롤러가 처리했는지, 핸들러 단위 처리 시간
AOP메서드 인자와 반환값, 예외서비스 메서드 단위의 입력·출력, 실행 시간

선택 기준은 무엇을 남기고 싶은지다. HTTP 수준의 액세스 로그나 요청 본문은 Filter가 맡는다. 로깅 필터를 Spring Security 필터 체인보다 앞에 두면 인증 단계에서 거부된 요청까지 남길 수 있다. Stack Overflow 답변이 요약하듯, 함수나 클래스의 값까지 남겨야 한다면 메서드 인자와 반환값에 직접 접근할 수 있는 AOP가 맞다.

요청 본문을 Filter에서 남길 때는 주의할 점이 있다. 서블릿 요청의 입력 스트림은 한 번만 읽을 수 있어서, 로깅을 위해 먼저 읽어 버리면 컨트롤러가 본문을 받지 못한다. Spring의 ContentCachingRequestWrapper로 요청을 감싸면 읽히는 내용을 캐시해 두었다가 나중에 꺼낼 수 있다. 다만 이 래퍼는 내용이 실제로 소비되어야 캐시하므로, 체인을 통과한 뒤(filterChain.doFilter 이후)에 꺼내야 한다(ContentCachingRequestWrapper Javadoc).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                FilterChain chain) throws ServletException, IOException {
    ContentCachingRequestWrapper req = new ContentCachingRequestWrapper(request);
    ContentCachingResponseWrapper res = new ContentCachingResponseWrapper(response);
    long start = System.currentTimeMillis();
    try {
        chain.doFilter(req, res);
    } finally {
        log.info("{} {} status={} took={}ms body={}",
                req.getMethod(), req.getRequestURI(), res.getStatus(),
                System.currentTimeMillis() - start,
                new String(req.getContentAsByteArray(), StandardCharsets.UTF_8));
        res.copyBodyToResponse();
    }
}

응답 래퍼를 쓰면 마지막에 copyBodyToResponse()를 호출해야 클라이언트에게 본문이 전달된다. 개인정보나 토큰이 본문에 들어 있을 수 있으므로 실제로는 마스킹과 길이 제한이 필요하다.

  • https://rollbar.com/blog/spring-boot-logging/
  • https://stackoverflow.com/questions/15746676/logging-with-aop-in-spring
  • https://www.baeldung.com/spring-boot-logging
  • https://stackoverflow.com/questions/49467489/logginginterceptor-or-aop-logging#:~:text=Logging%20via%20LoggingInterceptor%20is%20better,values%20of%20functions%20or%20classes.
  • https://koukia.ca/using-an-interceptor-for-a-cleaner-code-aop-c3c1f5e9d411
  • https://programmer.ink/think/springboot-foundation-of-microservice-series-filter-interceptor-and-aop-aspect.html
  • https://xzio.tistory.com/1999
  • https://velog.io/@miot2j/Spring-Filter-Interceptor-AOP-%EC%B0%A8%EC%9D%B4-%EB%B0%8F-AOP%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-Logging%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%9C-%EC%9D%B4%EC%9C%A0
참고한 자료외부 출처 1

외부 출처

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다