Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
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 31
Archives
Today
Total
관리 메뉴

csct3434

중앙 집중식 로깅 구현하기 (feat. Logback, CloudWatch Logs) 본문

개발 일지

중앙 집중식 로깅 구현하기 (feat. Logback, CloudWatch Logs)

csct3434 2024. 3. 30. 02:27

중앙 집중식 로깅의 필요성

AWS에서 오토 스케일링을 사용하는 경우, 다음과 같은 로그 관리의 어려움을 겪게 된다.

  • Scale out : 다중 서버로 인한 로그 분산
  • Scale in : 인스턴스 삭제로 인한 로그 분실

분산된 로그를 단일화된 저장소에서 관리하는 중앙 집중식 로깅(Centralized Logging)을 활용하여 이러한 문제점을 해결할 수 있다.

이 글에서는 logback-awslogs-appender 라이브러리와 AWS CloudWatch 서비스를 활용한 중앙 집중식 로깅 구현 방법에 대해 알아본다.


1. CloudWatch Log Group 생성

스프링 애플리케이션에서 전송된 로그를 보관하는 저장소인 CloudWatch Log Group을 생성해준다.

  • Log group name : 로그 저장소 이름
  • Retention setting : 로그 보관 기간
  • Log class : Standard / Infrequent Access
    • Log class는 한번 설정하면 수정이 불가능하다.
    • Infrequent Access는 새로 추가된 클래스인데, Standard에 비해 제한된 기능을 제공하지만 비용은 2배 저렴하다.
    • 상세 내용 : https://dev.classmethod.jp/articles/about-cloudwatch-ia-kr/
    • 참고 : Infrequenct Access 클래스를 사용해 봤는데, CloudWatch에서 로그를 바로 확인할 수 없어서 결국 Standard로 변경했다.
  • KMS key ARN : 암호화 시 선택

2. AWS IAM User 권한 설정

스프링 애플리케이션에서 앞서 생성한 CloudWatch Log Group에 접근하기 위해서는, CloudWatchFullAccessV2 권한을 보유한 AWS IAM User가 필요하다.

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/auth-and-access-control-cw.html#managed-policies-cloudwatch-CloudWatchFullAccessV2

[Add permissions]에서 CloudWatchFullAccess를 검색하면 'CloudWatchFullAccess'와 'CloudWatchFullAccessV2' 2개의 결과가 나온다. AWS 공식 문서에 따르면, CloudWatchFullAccess는 추후 deprecated될 예정이기 때문에 CloudWatchFullAccessV2의 사용을 권장하고 있다. CloudWatchFullAccessV2에는 EC2 Auto Scaling과 SNS에 대한 권한도 함께 포함되어 있다고 한다.


3. build.gradle 설정

implementation "ca.pjer:logback-awslogs-appender:1.6.0"

CloudWatch로 로그를 전송해주는 logback-awslogs-appender 라이브러리를 의존성에 추가해준다.

https://mvnrepository.com/artifact/ca.pjer/logback-awslogs-appender/1.6.0

해당 라이브러리는 2021년 이후로 업데이트가 중단되었으며, 가장 최신 버전인 1.6.0에는 CVE-2023-6378 취약점이 존재한다.

https://github.com/advisories/GHSA-vmq6-5m68-f53m

관련 내용을 찾아보니, logback receiver(원격 서버로부터 로그를 수신하는 컴포넌트)를 사용하는 경우 DOS 공격에 노출된다고 한다.

나의 경우 원격 서버(CloudWatch)로 로그를 전송하는 것이기 때문에 문제 없다고 판단했다.


4. logback.xml 작성

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
  <!-- AWS 자격증명 설정 -->
  <springProperty scope="context" name="AWS_ACCESS_KEY" source="cloud.aws.credentials.access-key"/>
  <springProperty scope="context" name="AWS_SECRET_KEY" source="cloud.aws.credentials.secret-key"/>

  <!-- 콘솔 로그 패턴 정의 -->
  <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
  <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
  <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
  <property name="CONSOLE_LOG_PATTERN" value="${LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){blue} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

  <!-- 콘솔에 로그를 출력해주는 ConsoleAppender 설정 -->
  <appender name="console_log" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${CONSOLE_LOG_PATTERN}</pattern>
    </encoder>
  </appender>

  <!-- CloudWatch에 로그를 전송해주는 AwsLogsAppender 설정 -->
  <appender name="aws_cloud_watch_log" class="ca.pjer.logback.AwsLogsAppender">
    <encoder>
      <pattern>[%thread] [%date] [%level] [%file:%line] - %msg%n</pattern>
    </encoder>
    <logGroupName>ec2-asg</logGroupName>
    <logStreamUuidPrefix>application-log-</logStreamUuidPrefix>
    <logRegion>ap-northeast-2</logRegion>
    <maxBatchLogEvents>50</maxBatchLogEvents>
    <maxFlushTimeMillis>30000</maxFlushTimeMillis>
    <maxBlockTimeMillis>5000</maxBlockTimeMillis>
    <retentionTimeDays>30</retentionTimeDays>
    <accessKeyId>${AWS_ACCESS_KEY}</accessKeyId>
    <secretAccessKey>${AWS_SECRET_KEY}</secretAccessKey>
  </appender>

  <!-- local 환경에서는 ConsoleAppender 사용 -->
  <springProfile name="local">
    <root level="INFO">
      <appender-ref ref="console_log"/>
    </root>
  </springProfile>

  <!-- prod 환경에서는 AwsLogsAppender 사용 -->
  <springProfile name="prod">
    <root level="INFO">
      <appender-ref ref="aws_cloud_watch_log"/>
    </root>
  </springProfile>
</configuration>
  • 내용이 많지만 하나씩 살펴보자!

  <!-- AWS 자격증명 설정 -->
  <springProperty scope="context" name="AWS_ACCESS_KEY" source="cloud.aws.credentials.access-key"/>
  <springProperty scope="context" name="AWS_SECRET_KEY" source="cloud.aws.credentials.secret-key"/>
  • 스프링 환경변수에 등록된 AWS IAM User의 Access Key와 Secret Key를 logback.xml의 변수로 등록하는 부분입니다.
  • CloudWatchFullAccessV2 권한이 부여된 IAM User의 Access Key와 Secret Key를 환경변수로 등록 후, 각각의 source에 적어주시면 됩니다.
  <!-- 콘솔 로그 패턴 정의 -->
  <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
  <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
  <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
  <property name="CONSOLE_LOG_PATTERN" value="${LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){blue} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

  <!-- 콘솔에 로그를 출력해주는 ConsoleAppender 설정 -->
  <appender name="console_log" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${CONSOLE_LOG_PATTERN}</pattern>
    </encoder>
  </appender>
  • 콘솔에 로그를 출력하는 ConsoleAppender를 설정하는 부분입니다.
  • 별도의 콘솔 로그 패턴을 정의하여 지정해주었고, ConsoleAppender의 이름은 'console_log'로 설정했습니다.
  <!-- CloudWatch에 로그를 전송해주는 AwsLogsAppender 설정 -->
  <appender name="aws_cloud_watch_log" class="ca.pjer.logback.AwsLogsAppender">
    <encoder>
      <pattern>[%thread] [%date] [%level] [%file:%line] - %msg%n</pattern>
    </encoder>
    <logGroupName>ec2-asg</logGroupName>
    <logStreamUuidPrefix>application-log-</logStreamUuidPrefix>
    <logRegion>ap-northeast-2</logRegion>
    <maxBatchLogEvents>50</maxBatchLogEvents>
    <maxFlushTimeMillis>30000</maxFlushTimeMillis>
    <maxBlockTimeMillis>5000</maxBlockTimeMillis>
    <retentionTimeDays>30</retentionTimeDays>
    <accessKeyId>${AWS_ACCESS_KEY}</accessKeyId>
    <secretAccessKey>${AWS_SECRET_KEY}</secretAccessKey>
  </appender>
  • CloudWatch로 로그를 전송하는 AwsLogsAppender를 설정하는 부분입니다.
    • logGroupName : AWS에서 생성한 CloudWatch Log Group의 이름
    • logStreamUuidPrefix : 로그 스트림의 UUID 앞에 붙을 prefix
    • logRegion : CloudWatch Log Group이 위치한 리전
    • maxBatchLogEvents : 로그 전송 단위 (로그 50개가 모일때 마다 로그를 전송)
    • maxFlushTimeMillis : 로그 전송 주기 (30초 마다 로그를 전송)
    • maxBlockTimeMillis
      • the maximum time the logging thread will wait for the logger
      • when maxBlockTimeMillis is 0, the logging thread will never wait for the logger, discarding events while the queue is full
      • 로그 버퍼가 가득찼을 때, 로그를 기록하는 쓰레드가 대기하는 최대 시간을 의미하는 것 같습니다.
    • retentionTimeDays : CloudWatch Log Group에서 설정한 로그 보관 기간
  • AwsLogsAppender의 이름은 'aws_cloud_watch_log'로 설정했습니다.
  <springProfile name="local">
    <root level="INFO">
      <appender-ref ref="console_log"/>
    </root>
  </springProfile>

  <springProfile name="prod">
    <root level="INFO">
      <appender-ref ref="aws_cloud_watch_log"/>
    </root>
  </springProfile>
  • 스프링 프로파일에서 사용할 Appender를 지정하는 부분입니다.
    • 로컬 환경 : 콘솔에 로그를 출력하기 위해 ConsoleAppender인 'console_log'를 설정했습니다.
    • 운영 환경 : CloudWatch로 로그를 전송하기 위해 AwsLogsAppender인 'aws_cloud_watch_log'를 설정했습니다.

5. 결과 확인

  • 로컬 환경에서는 콘솔에 로그가 출력되고, 운영 환경에서는 앞서 지정한 CloudWatch Log Group으로 로그가 전송됩니다.

  • 또한, SIGTERM 신호가 발생하더라도 애플리케이션이 종료될 때까지의 모든 로그가 전송되는 것을 확인할 수 있습니다.

참고한 글

https://devlog-wjdrbs96.tistory.com/329

https://waveofmymind.github.io/posts/aws-cloudwatch/

https://github.com/pierredavidbelanger/logback-awslogs-appender/blob/master/README.md