본문 바로가기
SpringBoot/구현 고민들

[Spring] 콘솔 로그를 어떻게 잘 관리할 수 있을까

by rla124 2024. 12. 14.

개발을 하다보면 트러블 슈팅을 해야할 일이 정말 많이 생긴다. 버그를 해결하는 과정에서 개발 테스트 상 스프링부트를 재실행하면 콘솔 로그가 초기화되면서 직전 로그 내역을 다른 곳에 기록해두지 않는 이상 확인을 할 수 없게 된다. 이뿐만 아니라 프론트에서 백엔드 서버로 API 요청 시 에러 사항에 대해서 확인하고 싶을 때 ec2에 접속해서 docker-compose logs(tail 옵션 붙여서 최근 로그를 바로 확인)를 통해 해당 에러가 언제 생겼는지 훑어야 하는 불편함이 존재한다.

 

따라서 로그의 영구적인 관리 중요성을 느끼고 WHOA 서비스에 (이슈 링크) 적용해보고자 했다. 먼저 콘솔 창에 로그를 출력할 때 System.out.println을 쓸 경우 성능 저하가 상당하기 때문에 loggerFactory를 이용하여 콘솔에 로그를 출력해왔다. 따라서 이 logger를 스프링부트 루트 디렉토리 경로에 별도로 log 파일 저장 폴더를 생성하여 날짜별로 발생한 로그를 저장해보고자 했다. 

 

logback 도입

이를 구현한 전체 pr 링크 바로가기

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggerUtil {
    public static final Logger logger = LoggerFactory.getLogger("프로젝트 명" || "로그 확인할 클래스 명");
}

위와 같이 loggerUtil을 별도로 정의를 해서

import static 프로젝트명.config.SecurityConfig.LoggerUtil.logger;

logger.info("디버깅할 값 : {}", value);

모든 service layer에서 import 하여 logger 객체를 사용하며 info, error, debug.. 등등 레벨에 따라서 적절한 값으로 가공이 되었는지 등에 대해 로그를 남겼었다.

 

로그를 파일로 관리하기 위해 spring-boot-starter-web에 자체적으로 내장이 되어있는 logback을 선택했다.

 

logback을 선택한 이유

Logback은 로깅 기능과 SLF4J(Simple Logging Facade for Java)를 통합한 로깅 솔루션이다. SLF4J는 다양한 로깅 구현체를 추상화하는 인터페이스를 제공한다면, Logback은 이러한 인터페이스를 구현하여 실제로 로그를 처리한다. Logback은 Log4j의 후속 버전으로 개발되었고 로거(Logger), 앱랜더(Appender), 레이아웃(Layout)이 모듈화된 아키텍쳐를 가지고 있으며 콘솔(Console), 파일(File), 데이터베이스(Database) 등 다양한 로그 출력 대상을 지원하기 때문에 프로젝트 내 로그 관리의 확장성을 보장받을 수 있을 것이라고 생각했다. 

 

logback-spring.xml 파일 

이 파일은 resources 폴더 내 application.yml || application.properties 와 같은 위치에 작성을 해야 했다. 

<configuration> 상위 태그 내에

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 이 태그를 통해서는 로그를 어떠한 format으로 출력을 할지 지정하였고

<appender name="log" class="ch.qos.logback.core.rolling.RollingFileAppender"> 이 태그를 통해서는 info, debug, error 등의 타입에 대해서 각각 어떤 파일 경로에 어떤 pattern으로 로그를 기록할지 작성해두었다. 

 

<configuration>
    <!-- Console 출력용 appender -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- INFO 로그 저장용 appender -->
    <appender name="log" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/LogData/info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/LogData/Log.log.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %level%n%m%n%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    .. 생략
    
</configuration>

 

 

앞으로 더 나아가 

이 로그파일을 확인하려면 운영 서버로 접속해서 스프링부트 루트 디렉토리로 접근하여 logs/LogData dir를 확인해야 한다. 보다 개발자 입장에서 로그 접근성을 확보하기 위해 db로 로그를 관리하는 방법으로 고도화를 해야겠다.

 

 

 

요새 일하면서 별도로 공부하시는 분들이 정말 대단하다고 생각한다... 

통신 과정에서 어떤 데이터를 내려 주었는지, 유지 보수를 위해서 프론트에서 어떤 리퀘스트를 보냈는지 확인해야하는 상황을 고려해서 엑세스 로그를 남기는 유틸을 추가할 예정이다.