Skip to content

Latest commit

 

History

History
217 lines (142 loc) · 9.75 KB

Spring Error log CloudWatch로 전송하기.md

File metadata and controls

217 lines (142 loc) · 9.75 KB

Spring Error log CloudWatch로 전송하는 법

서버를 관리하게 되면 가장 많이 하는 일 중 하나가 로그를 읽는 작업이라고도 할 수 있습니다. 로그는 그 당시 어떤 일이 일어났는지 확인할 수 있게 해주는 중요한 단서이기 때문에 문제가 생겼을 때 해결하는데 많은 도움이 됩니다. 그렇기 때문에 이런 로그들은 반드시 기록하고 있어야 하며 일정 기간 동안 유실되지 않도록 잘 관리해야 합니다.

또한 모든 로그를 다 기록할 순 없기 때문에 필요한 로그들만 잘 기록해야 하고, 많은 로그들 중에서 손쉽게 필요한 로그들만 찾을 수 있도록 관리할 방법들도 필요한데요. AWS에서는 이러한 기능들을 편리하게 사용할 수 있도록 CloudWatch라는 서비스를 제공해주고 있습니다. CloudWatch Agent가 로그도 모니터링해서 CloudWatch logs로 전송하는 역할도 하고 있습니다.

그래서 이번 글에서는 CloudWatchSpring Logback을 사용해서 Spring Error log를 CloudWatch Log Group 으로 전송하는 법에 대해서 정리 해보겠습니다.

이번 글의 실습을 진행하기 위해서는 반드시 IAM 설정 을 하고 오셔야 합니다.(CloudWatch 관련, CloudWatch Agent는 설치 안하셔도 될 거 같습니다.)


Spring 설정하기

Spring에서 CloudWatch로 Error log를 전송할 수 있도록 정말 편리하게 제공해주는 라이브러리 가 있습니다. 해당 라이브러리를 사용하려면 아래의 의존성을 추가해주어야 합니다.


gradle

compile group: 'ca.pjer', name: 'logback-awslogs-appender', version: '1.4.0'

maven

<dependency>
    <groupId>ca.pjer</groupId>
    <artifactId>logback-awslogs-appender</artifactId>
    <version>1.4.0</version>
</dependency>

위의 의존성을 추가하고 프로젝트 세팅을 해보겠습니다.


스크린샷 2021-05-13 오전 10 57 00

저의 프로젝트 구조는 위와 같습니다. 가장 중요한 파일은 logback.xml 파일 인데요. resources 폴더 아래에 logback.xml 파일을 만드시면 됩니다.


logback.xml

<configuration packagingData="true">

    <!-- Register the shutdown hook to allow logback to cleanly stop appenders -->
    <!-- this is strongly recommend when using AwsLogsAppender in async mode, -->
    <!-- to allow the queue to flush on exit -->
    <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>

    <!-- Timestamp used into the Log Stream Name -->
    <timestamp key="timestamp" datePattern="yyyyMMddHHmmssSSS"/>

    <!-- The actual AwsLogsAppender (asynchronous mode because of maxFlushTimeMillis > 0) -->
    <appender name="ASYNC_AWS_LOGS" class="ca.pjer.logback.AwsLogsAppender">

        <!-- Send only WARN and above -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>

        <!-- Nice layout pattern -->
        <layout>
            <pattern>%d{yyyyMMdd'T'HHmmss} %thread %level %logger{15} %msg%n</pattern>
        </layout>

        <!-- Hardcoded Log Group Name -->
        <logGroupName>Spring-log</logGroupName>

        <!-- Log Stream Name UUID Prefix -->
        <logStreamUuidPrefix>mystream/</logStreamUuidPrefix>

        <!-- Hardcoded AWS region -->
        <!-- So even when running inside an AWS instance in us-west-1, logs will go to us-west-2 -->
        <logRegion>ap-northeast-2</logRegion>

        <!-- Maximum number of events in each batch (50 is the default) -->
        <!-- will flush when the event queue has 50 elements, even if still in quiet time (see maxFlushTimeMillis) -->
        <maxBatchLogEvents>50</maxBatchLogEvents>

        <!-- Maximum quiet time in millisecond (0 is the default) -->
        <!-- will flush when met, even if the batch size is not met (see maxBatchLogEvents) -->
        <maxFlushTimeMillis>30000</maxFlushTimeMillis>

        <!-- Maximum block time in millisecond (5000 is the default) -->
        <!-- when > 0: this is the maximum time the logging thread will wait for the logger, -->
        <!-- when == 0: the logging thread will never wait for the logger, discarding events while the queue is full -->
        <maxBlockTimeMillis>5000</maxBlockTimeMillis>

        <!-- Retention value for log groups, 0 for infinite see -->
        <!-- https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutRetentionPolicy.html for other -->
        <!-- possible values -->

        <retentionTimeDays>0</retentionTimeDays>
    </appender>

    <!-- A console output -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyyMMdd'T'HHmmss} %thread %level %logger{15} %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Root with a threshold to INFO and above -->
    <root level="INFO">
        <!-- Append to the console -->
        <appender-ref ref="STDOUT"/>
        <!-- Append also to the (async) AwsLogsAppender -->
        <appender-ref ref="ASYNC_AWS_LOGS"/>
    </root>
</configuration>

전체의 코드는 위와 같은데요. 이 중에서 자세히 볼 부분에 대해서만 정리를 해보겠습니다.


스크린샷 2021-05-13 오전 11 02 19

이번 글에서는 이정도의 설정만 보면 될 거 같고 자세히 더 알고 싶다면 여기 를 참고하시면 될 것 같습니다. 그리고 Spring logback 관련으로 찾아서 보아도 좋을 거 같습니다.


Controller 만들기

스크린샷 2021-05-13 오전 11 06 15

@Slf4j
@RestController
public class HelloController {

    @GetMapping("/")
    public String hello() {
        log.error("에러입니다!");
        return "hello";
    }
}

저는 위와 같이 해당 API가 호출되면 error log를 출력하도록 만들어놓았습니다. 이게 설정 끝입니다! 정말 간단한 것 같습니다.(라이브러리의 힘...) 이제 프로젝트 jar를 만들어서 EC2로 업로드 해보겠습니다.


Spring jar 만들기

./gradlew clean build

스크린샷 2021-05-13 오전 11 08 37

그러면 위와 같이 jar 파일이 만들어지는데요. 저는 이것을 Filezila를 사용해서 EC2에 올리겠습니다. 간단하게 Filezila는 어떻게 사용하는지에 대해서도 정리해보겠습니다.


Filezila 사용법

스크린샷 2021-05-13 오전 11 10 34

스크린샷 2021-05-13 오전 11 13 41

위와 같이 하면 EC2로 jar 파일이 전송됩니다.


스크린샷 2021-05-13 오전 11 15 55

그리고 EC2에 접속해서 확인해보면 위와 같이 jar 파일이 존재하는 것을 확인할 수 있습니다. 바로 jar를 실행시키겠습니다.


sudo amazon-linux-extras install java-openjdk11 (제가 설치한 자바 11버전 명령어)
sudo nohup java -jar *.jar &

혹시 EC2에 자바가 설치되지 않았다면 java를 설치한 후에 위의 명령어를 사용하셔야 합니다.(저는 EC2 Linux2 버전입니다. java 8을 설치하고 싶다면 여기 를 참고하면 좋습니다.)


스크린샷 2021-05-13 오전 11 19 15

sudo netstat -tnlp

그러면 위의 명령어로 확인해보면 jar가 실행가 8080 포트에서 잘 실행되고 있는 것을 볼 수 있습니다.


스크린샷 2021-05-13 오전 11 21 03

그리고 해당 주소로 접속해보면 위와 같이 Controller에서 만든 대로 잘 응답이 오는 것도 확인할 수 있습니다. 위의 API 호출이 되었기 때문에 log.error()로 출력했던 로그가 CloudWatch로 전송이 되었을 것입니다. 정말 잘 되었는지 확인을 한번 해보겠습니다.


스크린샷 2021-05-13 오전 11 22 44

저는 위와 같이 CloudWatch에 Spring-log 라는 로그그룹이 존재하는데 여기로 log를 전송했습니다.


스크린샷 2021-05-13 오전 11 24 37

제가 몇번 호출했던 결과들이 CloudWatch 로그 그룹에 잘 출력이 되는 것을 확인할 수 있습니다.

자세한 코드는 Github 에서 확인하실 수 있습니다.