본문 바로가기
Java & 스프링

[spring] AOP를 이용하여 controller의 input data 자동 로그 기록하기

by hjhello423 2019. 12. 27.

스프링 부트에서 애플리케이션을 만들었을 때 로그기록이 상당히 귀찮았던 경험이 있을 거라고 생각합니다.

그런데 이 로깅을 자동으로 해주는 녀석이 있다면 얼마나 편해질까요?

스프링의 AOP(Aspect-Oriented Programming)를 이용하여 controller로 들어오는 data를 자동으로 logging 해주는 기능을 만들어 보도록 하겠습니다.

 


dependency 먼저

우선 aop를 이용하므로 aop의 dependency를 추가해 보겠습니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

 


어떻게 만들까?

AOP를 이용하여 모든 controller를 호출할 때 input data를 뽑아내고 그 data를 log로 기록하려고 합니다.

순서대로 무엇을 만들지 생각해보겠습니다.

 

1. 우선 AOP가 구현된 class를 만들어야 한다. 그리고 해당 class는 Bean으로 등록되어 있어야 동작할 것이다.

2. Aspect가 적용될 PointCut을 적용한다.

3. Aspect를 구현한다.

4. 언제 이 Aspect를 수행할지 결정할 Advice를 지정해야 한다. controller의 전후 작업을 위해 Around로 적용할 것이다.

 

 


구현

1번 과정을 구현해 보도록 하겠습니다.

먼저 class를 생성해보겠습니다.

로그를 기록하기 위해 Lombok의 @Slf4j, Bean으로 등록하기 위해 @Component를 사용하였으며 마지막으로 AOP를 적용시키기 위해 @Aspect를 사용했습니다.

@Slf4j
@Aspect
@Component
public class ControllerLogger {  
}

 

이번엔 2번 과정 구현을 위해 PointCut을 지정해 보겠습니다.

"execution(* com.hongjun423.toy_project.controller.*.*(..))" 에 대한 해석을 해보자면...

가장 처음 시작하는 *은 리턴 타입을 나타냅니다. 따라서 모든 형식의 리턴 타입을 포함한다는 의미입니다.

com.hongjun423.toy_project.controller.*.*(..) 이것은 class파일, 함수 등의 경로를 나타냅니다.

저는 com.hongjun423.toy_project.controller 경로에 모든 controller파일을 모아둘 예정이어서 이 경로 하위의 모든 함수를 지정하였습니다.

(...)은 모든 타입의 인자를 포함한다는 의미입니다.

@Pointcut("execution(* com.hongjun423.toy_project.controller.*.*(..))")
    public void controllerPointcut() {
}

 

자 이제 3번과 4번 과정이 남았습니다.

AOP와 관련된 부분만 집고 넘어가 보겠습니다.

나머지 부분은 코드를 보면 무슨 행위를 하는지 쉽게 이해할 수 있을 거라 생각됩니다.

 

먼저 @Around를 이용해 advice를 around로 지정하였고 위에서 작성한 @Pointcut이 적용된 메서드 이름을 지정하여 이 advice가 적용될 위치를 지정하였습니다.

around의 전후 개념은 joinPoint.proceed()를 기준으로 합니다.

따라서 joinPoint.proceed()가 선언되기 이전은 controller가 실행되기 전, 선언 이후는 controller 실행 후의 로직을 작성하게 됩니다.

따라서 아래의 코드는 컨트롤러 메서드를 실행하기 전에 전달받은 값을 로그로 기록한수 메서드를 실행하게 됩니다.

 

@Around("controllerPointcut()")
public Object doLogging(ProceedingJoinPoint joinPoint) throws Throwable {
    try {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        String controllerName = joinPoint.getSignature().getDeclaringType().getName();
        String methodName = joinPoint.getSignature().getName();
        Map<String, Object> params = new HashMap<>();

        try {
            params.put("controller", controllerName);
            params.put("method", methodName);
            params.put("params", getParams(request));
            params.put("log_time", LocalDateTime.now());
            params.put("request_uri", request.getRequestURI());
            params.put("http_method", request.getMethod());
        } catch (Exception e) {
            log.error("LoggerAspect error", e);
        }

        log.info("로그 내용 : {}", params);

        Object result = joinPoint.proceed();

        return result;
    } catch (Throwable throwable) {
        throw throwable;
    }
}


private static JSONObject getParams(HttpServletRequest request) throws JSONException {
    JSONObject jsonObject = new JSONObject();
    Enumeration<String> params = request.getParameterNames();
    while (params.hasMoreElements()) {
        String param = params.nextElement();
        String replaceParam = param.replaceAll("\\.", "-");
        jsonObject.put(replaceParam, request.getParameter(param));
    }
    return jsonObject;
}

최종 코드는?

@Slf4j
@Aspect
@Component
public class ControllerLogger {

    @Pointcut("execution(* com.hongjun423.toy_project.controller.*.*(..))")
    public void controllerPointcut() {
    }

    @Around("controllerPointcut()")
    public Object doLogging(ProceedingJoinPoint joinPoint) throws Throwable {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        String controllerName = joinPoint.getSignature().getDeclaringType().getName();
        String methodName = joinPoint.getSignature().getName();
        Map<String, Object> params = new HashMap<>();

        try {
            params.put("controller", controllerName);
            params.put("method", methodName);
            params.put("params", getParams(request));
            params.put("log_time", LocalDateTime.now());
            params.put("request_uri", request.getRequestURI());
            params.put("http_method", request.getMethod());
        } catch (Exception e) {
            log.error("LoggerAspect error", e);
        }

        log.info("controller - {}", params);
        Object result = joinPoint.proceed();

        return result;

    }

    private static JSONObject getParams(HttpServletRequest request) throws JSONException {
        JSONObject jsonObject = new JSONObject();
        Enumeration<String> params = request.getParameterNames();
        while (params.hasMoreElements()) {
            String param = params.nextElement();
            String replaceParam = param.replaceAll("\\.", "-");
            jsonObject.put(replaceParam, request.getParameter(param));
        }
        return jsonObject;
    }
}

 


참고

 

 

 

 

 

 

반응형

댓글