본문 바로가기
Java

Spring Boot에서 Logback(+ CloudWatch에 기록하기)

by ma_ro 2023. 8. 23.

로깅

로그를 사용하는 목적에는 에러나 장애가 발생할 때 해당 내용을 파악하기 위함과 사용자 로그를 분석 데이터로 활용하기 위함이 있다. 콘솔에서 출력되는 로그는 로그 수준을 높이거나 낮추는데 그 목적이 있고, 에러나 장애 혹은 로그 분석을 위해선 로그를 파일에 저장한다.

스프링에서 로깅

초기 스프링은 JCL(Jakarta Commons Logging)을 사용해서 로깅을 구현했다. 요즘에는 대표적으로 Log4j와 Logback으로 스프링 부트의 로그 구현체를 사용한다.
Logback은 log4j 이후에 출시된 Java 기반 Logging Framework 중 하나로 가장 널리 사용되고 있다. slf4j의 구현체이며 Spring Boot 환경이라면 spring-boot-starter-web 안에 spring-boot-starter-logging에 기본적으로 포함되어 있다.

slf4j

Simple Logging Facade For java. logger 추상체로써 다른 로깅 프레임워크가 접근할 수 있도록 도와주는 추상화 계층이다. 따라서 slf4j를 이용하면 코드를 일정하게 유지하면서 구현체의 전환을 통해 다른 로깅 프레임워크로의 전환을 쉽게 할 수 있다.

Logging 구현체 선택

JCL을 사용하면 기본적인 인터페이스인 Log와 Log 객체 생성을 담당하는 LogFactory만 구현하면 언제든지 로깅 구현체 교체가 가능하니 선택은 자유롭다. slf4j는 JCL이 가진 문제를 해결하기 위해 클래스 로더 대신에 컴파일 시점에서 구현체를 선택하도록 변경시키기 위해 도입된 것이다. Logback은 log4j에 비해 향상된 필터링 정책, 기능, 로그 레벨 변경 등에 대해 서버를 재시작할 필요 없이 자동 리로딩을 지원한다는 장점이 있다.

로그 레벨

Logback은 5단계의 로그 레벨을 가진다.

심각도 수준은 Trace < Debug < Info < Warn < Error 이다.

  • 📝 Trace : 모든 레벨에 대한 로깅이 추적되므로 개발 단계에서 사용함
  • ⚙️ Debug : 개발 단계에서 사용하며, SQL 로깅을 할 수 있음
  • ✅ Info : 운영에 참고할만한 사항, 중요한 비즈니스 프로세스가 완료됨
  • ⚠ ️Warn : 로직 상 유효성 확인, 예상 가능한 문제로 인한 예외 처리, 당장 서비스 운영에는 영향이 없지만 주의해야 할 부분
  • ⛔️ Error : 예상하지 못한 심각한 문제가 발생하는 경우, 즉시 조취를 취해야 할 수준의 레벨

Debug와 Trace 레벨은 많은 양의 로그가 쌓이므로 자칫 운영 단계에서 해당 레벨의 로깅을 할 경우 용량 감당이 안될 수 있다. 때문에 중요하지 않은 정보는 Debug 이하로 설정하고 로깅을 하지 않는 편이 좋다.

public class LogService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());

    public void log() {
        logger.trace("Trace");
        logger.debug("Debug");
        logger.info("Info");
        logger.warn("Warn");
        logger.error("Error");
    }
}
  • default log level은 INFO이기 때문에 trace와 debug는 출력되지 않는다.
  • 콘솔 로그 레벨을 변경하는 방법은 application.yml과 logback-spring.xml에서 설정할 수 있다. application.yml은 설정하는 난이도가 비교적 쉽지만, 세부적 설정을 위해서는 loback-spring.xml로 관리하는 편이 더 좋다.

logback 설정

logback-spring.xml은 콘솔, 파일, DB 등 로그를 출력하는 방법을 지정하는 appender와 출력할 곳을 지정하는 logger로 나눌 수 있다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOGS_ABSOLUTE_PATH" value="./logs" />

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOGS_ABSOLUTE_PATH}/logback.log</file>
        <encoder>
            <pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOGS_ABSOLUTE_PATH}/logback.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

    <root level="WARN">
        <appender-ref ref="STDOUT" />
    </root>

        <logger name="com.parfait.study.simplelogging.service.SomeService" level="INFO"/>
    <logger name="org.springframework.web" level="DEBUG">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    </logger><
</configuration>
  • 전역에서는 log level이 warn 이상인 로그만 남기고 있지만, SomeService의 로그는 info 이상이면 남기도록 한다.
<configuration>

  <springProperty name="host" source="logserver.host" defaultValue="http://dev-logserver.com"/>

  <appender name="REMOTE_LOG_SERVER" class="xxx.yyy.RemoteLogAppender">
    <remoteHost>${host}</remoteHost>
  </appender>

  <logger name="com.parfait.study.simplelogging.service.SomeService" level="INFO"/>

  <root level="warn">
    <appender-ref ref="REMOTE_LOG_SERVER"/>
  </root>
</configuration>
  • 원격의 로그 서버로 로그를 보내고 로그 서버의 주소를 application.yml에서 logserver.host: http://logserver.com으로 정의했다고 하였을 때 위와 같이 설정할 수 있다.

aws cloud watch 추가

dependencies 설정

dependencies {
    implementation "ca.pjer:logback-awslogs-appender:1.6.0"
    implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.0.0")
    implementation 'io.awspring.cloud:spring-cloud-aws-starter'
}
  • ca.pjer:logback-awslogs-appender : aws cloud watch 기록을 위한 logback-awslogs-appender 라이브러리
  • application.yaml 설정 파일에 변수 설정을 해두고 가져다 쓰려고 한다. 이때 springProperty로 key값을 가져오기 위해 spring-cloud-aws-starter 를 추가하였다.
  • spring-cloud-aws-dependencies는 버전 설정을 통해 이후 import 되는 관련 라이브러리들의 버전 설정을 따로 할 필요없도록 한다.

logback 설정 파일

<configuration>
    <springProperty name="ACCESS_KEY" source="spring.cloud.aws.credentials.access-key"/>
    <springProperty name="SECRET_KEY" source="spring.cloud.aws.credentials.secret-key"/>
    <springProperty name="LOG_REGION" source="spring.cloud.aws.cloudwatch.region"/>

    <appender name="AWS_LOGS" class="ca.pjer.logback.AwsLogsAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>warn</level>
        </filter>
        <logGroupName>로그 그룹 이름</logGroupName>
        <logStreamUuidPrefix>로그 파일 prefix</logStreamUuidPrefix>
        <logRegion>${LOG_REGION}</logRegion>
        <accessKeyId>${ACCESS_KEY}</accessKeyId>
        <secretAccessKey>${SECRET_KEY}</secretAccessKey>
    </appender>

    <root level="info">
        <appender-ref ref="AWS_LOGS"/>
    </root>
</configuration>
  • ThresholdFilter filter는 설정 level 이상의 경우에만 로그를 기록하도록 한다.
  • 위 설정에서는 root level이 info로 설정되어 있지만 실제 로그 기록은 warn 이상인 경우에만 기록된다.

aws 사용자 등록

aws cloud watch 접속을 위한 사용자 계정을 등록한다.

  • IAM > 사용자 > 사용자 생성
  • 정책은 아래와 같이 설정하여 최소 권한만 준다.
{
    "Statement": [
        {
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}
  • 해당 사용자의 액세스 키를 생성하여 aws 접속에 사용한다.
  • helm + k8s 환경에서는 env로 컨테이너의 secret에서 가져와서 사용하도록 하였다.

Ref.

https://www.sangkon.com/hands-on-springboot-logging/

https://meetup.nhncloud.com/posts/149?ref=sangkon.com

https://docs.awspring.io/spring-cloud-aws/docs/3.0.0-M1/reference/html/appendix.html

https://docs.awspring.io/spring-cloud-aws/docs/3.0.2/reference/html/index.html#spring-cloud-aws-core

https://aws.amazon.com/ko/blogs/korea/getting-started-with-spring-boot-on-aws/

https://github.com/pierredavidbelanger/logback-awslogs-appender

'Java' 카테고리의 다른 글

Java 멀티 스레딩 및 성능 최적화(1)  (0) 2023.08.28

댓글