일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- chart.js
- codebuild
- Spring
- SQL
- docker
- bootstrap
- Airflow
- PostgreSQL
- Jenkins
- codepipeline
- codedeploy error
- kubeflow
- Flux
- JavaScript
- node
- AWS CI/CD 구축하기
- redis
- IntelliJ
- Kafka
- VPN
- COALESCE
- aws cicd
- chartjs
- java bigdecimal
- Python
- codedeploy
- or some instances in your deployment group are experiencing problems.
- Spring Error
- aws
- 도커
- Today
- Total
Small Asteroid Blog
[spring] api 호출 시 response, request 값 로그에 출력하기 - AOP 활용한 logging 출력 본문
[spring] api 호출 시 response, request 값 로그에 출력하기 - AOP 활용한 logging 출력
작은소행성☄️ 2023. 3. 20. 11:11
api 를 호출할 때마다 response, request 값의 로그를 log.info() 를 통해 작성하는 것이 비효율적이라고 느껴져서
공통적인 모듈을 사용해서 api 호출할때마다 로그를 안찍어도 출력될 수 있게 사용하고 싶었다.
로그를 남겨서 에러가 발생했을 때 빠른 대처와 api 흐름이 정상 동작하고 있는지에 대해 파악하기에도 좋다.
build.gradle 에 의존성 추가
// aop
implementation 'org.springframework.boot:spring-boot-starter-aop'
//Joiner
implementation 'com.google.guava:guava:31.1-jre'
aop 를 사용하기 위해 의존성을 추가해준다.
코드에서 joiner 를 사용하기 위해선 관련 의존성도 추가한다.
버전은 아래에서 골라서 사용해도 된다.
https://mvnrepository.com/artifact/com.google.guava/guava
어노테이션 추가
@EnableAspectJAutoProxy
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
@EnableAspectJAutoProxy
해당 어노테이션을 사용해서 AOP 를 사용할 수 있게 한다.
해당 어노테이션을 사용하면 스프링이 자동으로 개발자의 메소드를 호출하기 전에 가로챌 수 있도록 하는 기능이다.
코드 작성
LogAspect.java
package com.test.rewards.config.aop;
import com.google.common.base.Joiner;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.stream.Collectors;
@Aspect
@Component
public class LogAspect {
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Around("within(com.test.rewards.restapi..*)")
public Object logging(ProceedingJoinPoint pjp) throws Throwable {
String params = getRequestParams();
long startAt = System.currentTimeMillis();
logger.info("----------> REQUEST : {}({}) = {}", pjp.getSignature().getDeclaringTypeName(),
pjp.getSignature().getName(), params);
Object result = pjp.proceed();
long endAt = System.currentTimeMillis();
logger.info("----------> RESPONSE : {}({}) = {} ({}ms)", pjp.getSignature().getDeclaringTypeName(),
pjp.getSignature().getName(), result, endAt-startAt);
return result;
}
// get requset value
private String getRequestParams() {
String params = "";
RequestAttributes requestAttribute = RequestContextHolder.getRequestAttributes();
if(requestAttribute != null){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
Map<String, String[]> paramMap = request.getParameterMap();
if(!paramMap.isEmpty()) {
params = " [" + paramMapToString(paramMap) + "]";
}
}
return params;
}
private String paramMapToString(Map<String, String[]> paramMap) {
return paramMap.entrySet().stream()
.map(entry -> String.format("%s -> (%s)",
entry.getKey(), Joiner.on(",").join(entry.getValue())))
.collect(Collectors.joining(", "));
}
}

@Around("within(com.bearbetter.rewards.restapi..*)")
특정 클래스 안에 있는 메서드들 모두를 지정하는 패턴을 작성할 수 있는 방법이다.
패키지명까지가 아니라 클래스명까지 지정할 수 있는 것을 유의해야한다.
within(패키지.패키지.패키지.패키지.클래스)에서
within 안에 사용될 클래스 범위의 경우
Controller 를 가지고 있는 파일 위치까지 적어주면 된다.
결과 확인
아무 API 나 호출하면 다음과 같은 결과가 나오는 것을 확인할 수 있다.
