# AWS Xray -spring boot에서 aop를 이용해 xray 구성하기

### 시작하기 <a href="#undefined" id="undefined"></a>

API 호출을 많이 당하고? 있는 서버가 있는데, 문득 어떤 API를 많이 호출하고 있는지 확인 해보고 싶어져서 AWS의 x-ray를 사용해서 tracing 해보고 싶어졌다

Spring boot, aop, aws x-ray 를 사용해서 api를 tracing 해보자.\
(해당 포스팅은 로컬 mac환경에서 테스트해 본 것을 기준으로 할 꺼다)

***

### X-RAY 데몬 설치하기 <a href="#x-ray" id="x-ray"></a>

x-ray sdk가 전송하는 데이터가 x-ray 서비스에 도달하려면 데몬이 실행 중 이어야 한다.\
xray 데몬을 설치하기 전에 aws x-ray 서비스에 데이터를 전송할 권한이 있어야하는데, 나는 로컬에서 할 것이기 때문에 로컬에다가 자격증명(credentials) 하면된다.

aws iam에서 xray 권한을 가진 user를 만들고, cli로 credentials 해준다.

```
$ aws configure
AWS Access Key ID [None]: 엑세스키
AWS Secret Access Key [None]: 시크릿키
Default region name [None]: ap-northeast-2
Default output format [None]: text
```

다음 아래 xray 데몬을 다운 받아야하는데

[AWS X-Ray 데몬](https://docs.aws.amazon.com/ko_kr/xray/latest/devguide/xray-daemon.html#xray-daemon-downloading)

위 가이드에서 참고하여 다운로드 후 압축해제 해준다.\
(나의 로컬 환경은 OS X라서 aws-xray-daemon-macos-3.x.zip(sig) 를 다운)

이제 다운받은 데몬을 각 환경에 맞게 실행해주면 된다.

[로컬에서 X-Ray 데몬 실행](http://docs.aws.amazon.com/ko_kr/xray/latest/devguide/xray-daemon-local.html)

```
./xray_mac -o -n ap-northeast-2
```

이렇게 실행하면 아래와 같이 2000 port로 실행 성공한 것을 확인 할 수 있다.

```
[Info] Initializing AWS X-Ray daemon 3.2.0
[Info] Using buffer memory limit of 81 MB
[Info] 1296 segment buffers allocated
[Info] Using region: ap-northeast-2
[Info] HTTP Proxy server using X-Ray Endpoint : https://xray.ap-northeast-2.amazonaws.com
[Info] Starting proxy http server on 127.0.0.1:2000
```

***

### Spring boot에서 dependencies 추가 <a href="#spring-boot-dependencies" id="spring-boot-dependencies"></a>

이제 x-ray 트레이싱을 위해 gradle에 dependency를 추가해 주자.

[Java용 AWS X-Ray SDK](https://docs.aws.amazon.com/ko_kr/xray/latest/devguide/xray-sdk-java.html#xray-sdk-java-dependencies)

```
// xray 관련
dependencyManagement {
	imports {
		mavenBom('com.amazonaws:aws-xray-recorder-sdk-bom:2.4.0')
	}
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-aop'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	annotationProcessor 'org.projectlombok:lombok'

	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	compile 'com.h2database:h2'

	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}

  // xray 관련
	compile("com.amazonaws:aws-xray-recorder-sdk-core")
	compile("com.amazonaws:aws-xray-recorder-sdk-aws-sdk")
	compile("com.amazonaws:aws-xray-recorder-sdk-aws-sdk-instrumentor")
	compile("com.amazonaws:aws-xray-recorder-sdk-apache-http")
	compile("com.amazonaws:aws-xray-recorder-sdk-spring")
}
```

디펜던시 추가 시 datasource 가 필수인듯(추후에 다시 설명) 해서 jpa도 추가하고, 프로퍼티에서 datasource도 설정해준다.

aop를 사용해서 트레이싱할 예정이니 aop도 추가해주자.

***

### 전역 레코더 구성, TracingFilter 설정 <a href="#tracingfilter" id="tracingfilter"></a>

아래 코드를 생성하여 AWSXRayRecorderBuilder를 전역에 설정해주고,\
샘플링 룰을 설정할 수 있는 sampling-rules.json 파일도 생성해준다.

이 레코더가 json형태의 데이터(세그먼트)를 생성하여 데몬에 전달하는 듯 하다.\
세그먼트를 생성할 때의 규칙을 해당 설정 json파일(sampling-rules.json)에 정의하면\
허용 url, 허용 method, rate 등 세그먼트에 대한 규칙을 설정할 수 있다.

```
package com.isntyet.test.aws.xray.config;

import com.amazonaws.xray.AWSXRay;
import com.amazonaws.xray.AWSXRayRecorderBuilder;
import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
import com.amazonaws.xray.plugins.EC2Plugin;
import com.amazonaws.xray.strategy.sampling.CentralizedSamplingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.net.URL;

@Configuration
public class WebConfig {
    static {
        AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder.standard().withPlugin(new EC2Plugin());

        URL ruleFile = WebConfig.class.getResource("/sampling-rules.json");
        builder.withSamplingStrategy(new CentralizedSamplingStrategy(ruleFile));
        AWSXRay.setGlobalRecorder(builder.build());
    }

    @Bean
    public Filter TracingFilter() {
        return new AWSXRayServletFilter("JoTest");
    }
}
```

* /src/main/resources/sampling-rules.json

```
{
  "version": 1,
  "default": {
    "fixed_target": 1,
    "rate": 1
  }
}
```

[X-Ray 콘솔에서 샘플링 규칙 구성](https://docs.aws.amazon.com/ko_kr/xray/latest/devguide/xray-console-sampling.html)

TracingFilter 에서는 AWSXRayServletFilter를 통해서 세그먼트 이름을 fix 할 수 있다.

***

### XrayInspector 생성 (aop) <a href="#xrayinspector-aop" id="xrayinspector-aop"></a>

aop를 활용하면 특별한 로직을 추가하지 않아도 쉽게 설정할 수 있다.

* [Spring의 AOP 및 X-Ray SDK for Java](https://docs.aws.amazon.com/ko_kr/xray/latest/devguide/xray-sdk-java-aop-spring.html)
* aop 활성화

  ```
  @SpringBootApplication
  @EnableAspectJAutoProxy
  public class XrayApplication {

  	public static void main(String[] args) {
  		SpringApplication.run(XrayApplication.class, args);
  	}
  }
  ```

  aop를 활성화 해주고
* XrayInspector

  ```
  package com.isntyet.test.aws.xray.interceptor;

  import com.amazonaws.xray.entities.Subsegment;
  import com.amazonaws.xray.spring.aop.AbstractXRayInterceptor;
  import org.aspectj.lang.ProceedingJoinPoint;
  import org.aspectj.lang.annotation.Aspect;
  import org.aspectj.lang.annotation.Pointcut;
  import org.springframework.stereotype.Component;

  import java.util.Map;

  @Aspect
  @Component
  public class XrayInspector extends AbstractXRayInterceptor {
      @Override
      protected Map<String, Map<String, Object>> generateMetadata(ProceedingJoinPoint proceedingJoinPoint, Subsegment subsegment) {
          return super.generateMetadata(proceedingJoinPoint, subsegment);
      }

      @Override
      @Pointcut("@within(com.amazonaws.xray.spring.aop.XRayEnabled) && bean(*Controller)")
      public void xrayEnabledClasses() {
      }
  }
  ```

  AbstractXRayInterceptor 를 상속받은 XrayInspector를 구현해준다.

  xrayEnabledClasses 포인트컷에서 설정한 것 처럼 XRayEnabled 설정된 Controller bean이 래핑되어 해당 컨트롤러에서 추적이 될것이다.

  이전에 datasource 설정이 필요하다고 했는데 이유는 AbstractXRayInterceptor 를 들어가보면

  ```
  @Pointcut("execution(public !void org.springframework.data.repository.Repository+.*(..))")
  protected void springRepositories() {
  }
  ```

  부분에서 data repository 가 포인트컷에 들어가 있기 때문에 datasource 설정을 해주지 않으면 실행이 되지 않는다ㅡㅡ;;

***

### 테스트 controller 만들기 <a href="#controller" id="controller"></a>

이제 테스트로 호출해 볼 api가 필요하니 controller를 구성해보자.

* XrayController

  ```
  @RestController
  @XRayEnabled
  public class XrayController {

    private XrayService xrayService;

    public XrayController(XrayService xrayService){
        this.xrayService = xrayService;
    }

    @GetMapping("/test")
    public String test(){
        return "test";
    }

    @GetMapping("/real")
    public String real(){
        String testStr = xrayService.testStr();
        System.out.println(testStr);

        return testStr;
    }
  }
  ```

대충 스트링이 반환되게 구성하고 @XRayEnabled 를 꼭 기입하여 xray 활성화를 시켜준다.

***

### API 테스트 <a href="#api" id="api"></a>

이제 준비가 끝났으니 만들어진 api를 호출해보면 이전에 실행해놓은 데몬에 로그가 찍힐것이다.

* 데몬 로그

  ```
  [Info] 1296 segment buffers allocated
  [Info] Using region: ap-northeast-2
  [Info] HTTP Proxy server using X-Ray Endpoint : https://xray.ap-northeast-2.amazonaws.com
  [Info] Starting proxy http server on 127.0.0.1:2000
  [Info] Successfully sent batch of 1 segments (0.632 seconds)
  [Info] Successfully sent batch of 1 segments (0.027 seconds)
  ```

  이렇게 ‘Successfully’ 라고 뜨면 성공한것이다.
* aws의 x-ray 서비스 콘솔의 트레이스 메뉴

  [<img src="https://drive.google.com/uc?id=1eKLTkB0FfVRHwK1APisfdtQVGMvxeDb5" alt="" data-size="original">](https://drive.google.com/uc?id=1eKLTkB0FfVRHwK1APisfdtQVGMvxeDb5)

  위와 같이 트레이싱 되어 url, 평균응답시간, 트레이스 비율 등을 볼 수 있다.

출처 : <https://isntyet.github.io/java/spring-boot%EC%97%90%EC%84%9C-aop%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-xray-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0/>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://taejun.gitbook.io/tech/12-aws/aws-xray-spring-boot-aop-xray.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
