[Logging] 내 프로젝트에 Logging 적용해서 로그 출력하기

2025. 1. 13. 14:32·Spring/Logging
728x90
반응형
SMALL

2025.01.06 - [Spring/Logging] - [Logging] 로그 출력을 효율적으로 생성 및 추적하기 7편 - Spring AOP

2025.01.06 - [Spring/Logging] - [Logging] 로그 출력을 효율적으로 생성 및 추적하기 6편 - Proxy

2025.01.06 - [Spring/Logging] - [Logging] 로그 출력을 효율적으로 생성 및 추적하기 5편 - Template Method

2025.01.06 - [Spring/Logging] - [Logging] 로그 출력을 효율적으로 생성 및 추적하기 4편 ThreadLocal사용기

2024.12.26 - [Spring/Logging] - [Logging] 로그 출력을 효율적으로 생성 및 추적하기 3편

2024.12.25 - [Spring/Logging] - [Logging] 로그 출력을 효율적으로 생성 및 추적하기 2편

2024.12.25 - [Spring/Logging] - [Logging] 로그 출력을 효율적으로 생성 및 추적하기 1편

 

 

스프링에서 제공하는 Spring AOP를 활용해서 로깅이라는 공통 관심사를 분리하여 공통 로직을 하나의 클래스에서 관리하도록 하는 방식을 드디어 적용해 보았다. 

@Pointcut으로 AOP가 적용될 시점을 Controller와 Service가 포함된 클래스로 지정하고 @Around로  수행시점의 메서드와 매개변수값, 요청 uri 등을 출력하도록 했다. 예민한 정보인 토큰값, 계정 비밀번호, 키 값들은 출력이 안되도록 설정 후 나머지 내용들에 대해서 로그를 출력하도록 했다. 

서비스는 @AfterReturning 어노테이션을 활용해서 종료시 반환값 또한 출력하도록 설정해 줬다.

스프링 AOP가 없었다면 CGLIB 또는 동적 JDK 프록시를 사용해서 상황에 맞게 커스텀해줘야 하는 번거로움이 생겼을 텐데 이러한 편리함을 주는 프레임워크가 있어 정말 다행이었다.

 

traceId를 직접 부여하고 로그를 출력해 보는 방법부터 디자인패턴인 Method Template, Startegy 전략, 동적 프록시 등 다양한 방법을 수행해 보면서 느꼈던 점은 자바의 객체지향적인 특성을 살리되 상속을 최대한 피하고 합성을 해야 하며, 객체 간의 의존성을 최대한 약화시켜결합도를 낮추고 응집도를 높여야 한다는 점이었다. 객체간의 영향이 아예 없을 순 없지만 서로 간의 의존성이 낮을수록 코드의 유연성, 유지 보수성 등이 이렇게 향상될 수 있구나 하는 점을 느꼈다.

 

    @Pointcut("execution(* pupket.togedogserver.domain.*.controller.*.*(..))")
    private void cut() {}

    @Around("cut()")
    public Object aroundLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        // 메서드 정보 받아오기
        Method method;
        String uri;
        try{
             method = getMethod(proceedingJoinPoint);
            uri = getRequestURI();
        }catch (Exception e){
            return proceedingJoinPoint.proceed();
        }

        // 요청 URI 확인
        if ("/health-check".equals(uri)) {
            return proceedingJoinPoint.proceed(); // /health-check API는 로그 출력하지 않음
        }

        // 클래스 정보 받아오기
        log.info("class name = {}", proceedingJoinPoint.getTarget().getClass().getName());
        log.info("======= method name = {} =======", method.getName());

        // 파라미터 받아오기
        Object[] args = proceedingJoinPoint.getArgs();
        if (args.length == 0) {
            log.info("no parameter");
        } else {
            for (Object arg : args) {
                if (arg == null) {
                    log.info("parameter = null");
                } else {
                    log.info("parameter type = {}", arg.getClass().getSimpleName());
                    log.info("parameter value = {}", arg);
                }
            }
        }

        // 메서드 실행 및 결과 로깅
        Object returnObj = proceedingJoinPoint.proceed();
        if (returnObj == null) {
            log.info("return value = null");
        } else {
            log.info("return type = {}", returnObj.getClass().getSimpleName());
            log.info("return value = {}", returnObj);
        }

        return returnObj;
    }

    private Method getMethod(ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        return signature.getMethod();
    }

    private String getRequestURI() {
        // 현재 스레드의 요청 URI를 가져오는 코드
        return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
                .getRequest().getRequestURI();
    }

    // Service의 메서드를 포인트컷으로 지정
    @Pointcut("within(pupket.togedogserver.domain..*Service)")
    public void service() {}

    // Service 메서드 호출 전후 로깅
    @Around("service()")
    public Object loggingService(ProceedingJoinPoint joinPoint) throws Throwable {
         // RedisSortedSetService의 addToSortedSet 메소드는 로깅 제외
         if (joinPoint.getSignature().getDeclaringType().getSimpleName().equals("RedisSortedSetService") 
         && joinPoint.getSignature().getName().contains("addToSortedSet")) {
         return joinPoint.proceed();
     }
        
        String serviceName = joinPoint.getSignature().getDeclaringType().getSimpleName();
        String methodName = joinPoint.getSignature().getName();

        // 서비스 시작 로그
        log.info("Service method started: {}.{}()", serviceName, methodName);
        
        // 메서드 파라미터 로깅 (민감한 정보 제외)
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] != null && !containsSensitiveData(args[i])) {
                    log.info("Arg[{}]: {}", i, args[i]);
                }
            }
        }

        long startTime = System.currentTimeMillis();
        Object result = null;
        
        try {
            result = joinPoint.proceed();
            return result;
        } finally {
            long elapsedTime = System.currentTimeMillis() - startTime;
            log.info("Service method finished: {}.{}() [Execution time: {} ms]", 
                    serviceName, methodName, elapsedTime);
            
            // 결과 로깅 (민감한 정보 제외)
            if (result != null && !containsSensitiveData(result)) {
                log.info("Return value: {}", result);
            }
        }
    }

    private boolean containsSensitiveData(Object obj) {
        // 민감한 정보를 포함하는 객체인지 확인하는 로직
        return obj.toString().contains("password") || 
               obj.toString().contains("token") ||
               obj.toString().contains("key");
    }

    @AfterReturning(pointcut = "service()", returning = "returnValue")
    public void afterReturningServiceLogging(JoinPoint joinPoint, Object returnValue) {
        if (joinPoint.getSignature().toShortString().contains("addToSortedSet")) {
            return;
        }
        log.info("### Service method finished: {}", joinPoint.getSignature().toShortString());
        if (returnValue != null) {
            log.info("Service return value: {}", returnValue);
        }
    }
728x90
반응형
SMALL

'Spring > Logging' 카테고리의 다른 글

[Logging] 로그 출력을 효율적으로 생성 및 추적하기 7편 - Spring AOP  (0) 2025.01.06
[Logging] 로그 출력을 효율적으로 생성 및 추적하기 6편 - Proxy  (1) 2025.01.06
[Logging] 로그 출력을 효율적으로 생성 및 추적하기 5편 - Template Method  (1) 2025.01.06
[Logging] 로그 출력을 효율적으로 생성 및 추적하기 4편 ThreadLocal사용기  (0) 2025.01.06
[Logging] 로그 출력을 효율적으로 생성 및 추적하기 3편  (1) 2024.12.26
'Spring/Logging' 카테고리의 다른 글
  • [Logging] 로그 출력을 효율적으로 생성 및 추적하기 7편 - Spring AOP
  • [Logging] 로그 출력을 효율적으로 생성 및 추적하기 6편 - Proxy
  • [Logging] 로그 출력을 효율적으로 생성 및 추적하기 5편 - Template Method
  • [Logging] 로그 출력을 효율적으로 생성 및 추적하기 4편 ThreadLocal사용기
공부하고 기억하는 공간
공부하고 기억하는 공간
IT 비전공자로 시작하여 훌륭한 개발자가 되기 위해 공부하고 있는 공간입니다. 틀린 내용이나 부족한 부분이 있으면 댓글로 알려주세요 바로 수정하겠습니다.
    250x250
  • 공부하고 기억하는 공간
    IT - railroad
    공부하고 기억하는 공간
  • 전체
    오늘
    어제
    • 분류 전체보기 (325)
      • 면접 준비 (22)
        • OS (6)
        • Spring Security (0)
        • Java (3)
        • DB (11)
        • Network (3)
      • ElasticSearch (2)
      • Kafka (4)
      • Spring (22)
        • Spring Cloud (7)
        • Security6 (5)
        • JPA (12)
        • 프로젝트 리팩토링 회고록 (4)
        • Logging (8)
        • Batch (2)
      • Redis (17)
        • Redis 개념 (8)
        • Redis 채팅 (5)
        • Redis 읽기쓰기 전략 (1)
      • AWS (11)
      • 리눅스 (29)
        • 리눅스 마스터 2급 (5)
        • 네트워크(기초) (7)
        • 리눅스의 이해 (6)
        • 리눅스의 설치 (2)
        • 리눅스 운영 및 관리 (6)
      • JAVA-기초 (16)
        • JAVA기본 (11)
        • Design Pattern (5)
      • JSP (27)
        • JSP 기본 개념 (10)
        • JSP (1)
      • SQL (1)
      • TIL (36)
      • 문제 풀이 (2)
        • Programmers (9)
        • 백준 문제풀이 (28)
      • JavaScript (10)
      • HTML (17)
      • Ngrinder (1)
        • Ngrinder 문서 정리 (1)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      자바
      CSS
      Til
      리눅스
      프로그래머스
      Spring
      HTML
      springsecurity
      레디스
      스프링프레임워크
      spring redis
      자바 알고리즘
      jsp request
      java
      JS
      Spring Data Redis
      자바 반복문
      jsp기초
      리눅스마스터2급정리
      자바 면접
      리눅스마스터2급
      자바스크립트
      JSP
      redis
      자바기초
      백준
      redis 채팅
      자바 면접질문
      Springframework
      JavaScript
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    공부하고 기억하는 공간
    [Logging] 내 프로젝트에 Logging 적용해서 로그 출력하기
    상단으로

    티스토리툴바