티스토리 뷰

Spring

Slf4j로 로깅을 해보자

기억용블로그 2022. 7. 29. 16:21
728x90

Slf4j는 Simple Logging Facade for Java의 acronym으로

파사드 패턴을 이용한 로깅 인터페이스라는 의미 정도로 해석할 수 있을 것이다.

 

Slf4j의 구조

 

Slf4j라는 파사드 인터페이스를 앞에 세워두고 Application과 로깅 프레임워크간의 직접적인 결합도를 없애고 파사드 하나만을 바라보게 해서 로깅 구현체가 어떤 식으로 변경되든 변화를 무시 혹은 최소화할 수 있다.

 

System.out.println을 지양해야 하는 이유

성능을 떠나서 System.out.println는 콘솔창(표준 출력)으로 결과를 남기지만 로깅이 의미를 가지려면 파일의 형태로 저장이 되어야 하므로 로깅의 개념으로 사용이 불가능하다. 

(방법이야 찾아보면 가능하겠지만 성능의 오버헤드를 생각하면..)

 

System.out.println의 가장 큰 문제점은 blocking IO라는 것이다.

콘솔창에 로그를 출력하는 동안 다른 작업들이 계속 대기해야 한다는 의미로 적은 양의 트래픽을 처리할 때는 크게 성능 차이를 보이지 않지만 트래픽이 증가할수록 성능 오버헤드는 log에 비해 exponential하게 증가하게 될 것이다.

 

System.out.println이 좀 더 cpu usage가 높다고 얘기하는데 이쪽보다 비동기, 동기 차이가 가장 큰 차이점을 불러온다고 생각한다.

 

기본적인 설정

간단한 설정만 필요한 경우 다음과 같이 기본적인 설정만으로도 사용이 가능하다.

다음 코드는 application.properties에 들어가는 내용이다.

#Logs
logging.level.root=debug
#logging.file.name=
#logging.file.max-size=
#logging.file.max-history=

 

각 설정에 대한 설명은 다음 표로 대체한다. (출처는 #레퍼런스에 표기해두었다.)

 

 

좀 더 복잡한 내용이 필요하다면 logback-spring.xml 파일을 생성해서 사용한다.

(이미 누군가가 만들어두었기 때문에 갖다 쓰기만 하면 돼서 이쪽이 오히려 더 편하다.)

 

logback.xml은 spring이 뜨기 전에 이미 모든 설정 파일을 불러들여 읽기 때문에 application.properties를 사용할 수 없다.

logback-spring.xml으로 작성한다면 spring이 다 뜨고나서 (== application.properties이 뜨고 나서) logback-spring.xml 파일이 작성되기에 환경마다 다르게 들어가야 하는 값을 주입시킬 수 있다.

 

application.properties

 

log.config.path=./logs/local
log.config.filename=local_logs

 

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 60초마다 설정 파일의 변경을 확인 하여 변경시 갱신 -->
<configuration scan="true" scanPeriod="60 seconds">
    <springProfile name="local">
        <property resource="application.properties"/>
    </springProfile>
    <springProfile name="dev">
        <property resource="application-dev.properties"/>
    </springProfile>
    <springProfile name="prod">
        <property resource="application-prod.properties"/>
    </springProfile>
    <springProperty scope="context" name="LOG_LEVEL" source="logging.level.root"/>

    <!-- log file path -->
    <property name="LOG_PATH" value="${log.config.path}"/>
    <!-- log file name -->
    <property name="LOG_FILE_NAME" value="${log.config.filename}"/>
    <!-- err log file name -->
    <property name="ERR_LOG_FILE_NAME" value="err_log"/>
    <!-- pattern -->
    <property name="LOG_PATTERN" value="%-5level %d{yy-MM-dd HH:mm:ss}[%thread] [%logger{0}:%line] - %msg%n"/>

    <!-- Console Appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- File Appender -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 파일경로 설정 -->
        <file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>

        <!-- 출력패턴 설정-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>

        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
            <fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 파일당 최고 용량 kb, mb, gb -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
            <maxHistory>30</maxHistory>
            <!--<MinIndex>1</MinIndex>
            <MaxIndex>10</MaxIndex>-->
        </rollingPolicy>
    </appender>

    <!-- 에러의 경우 파일에 로그 처리 -->
    <appender name="Error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <file>${LOG_PATH}/${ERR_LOG_FILE_NAME}.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
        <!-- Rolling 정책 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
            <fileNamePattern>${LOG_PATH}/${ERR_LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 파일당 최고 용량 kb, mb, gb -->
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- root레벨 설정 -->
    <root level="${LOG_LEVEL}">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="Error"/>
    </root>

</configuration>

 

어디를 로깅해야 하는가?

이 문제는 정답이 있는 것도 아니고 규모와 목적에 따라 너무 달라지기에 정보를 얻기가 쉽지 않았다.

여러 글을 읽고 내린 결론은 비즈니스 목적의 로깅과 유지보수 목적의 로깅과 같이 아예 다른 범주의 여러 로깅이 있으며 

목적에 따라 로깅의 방식과 빈도가 달라져야 한다는 생각이 들었다.

 

유지보수를 목적으로 로깅을 한다면 조금이라도 만들어놓은 흐름을 벗어나는 모든 순간을 로깅하고 이후에 리팩토링을 통해 로깅을 줄이는 방향으로 가는 것이 좋은 것같다.

e.g 로그인 성공, 실패, 회원가입 성공, 실패, 메일 전송 실패, 성공 등 단순한 GET 요청을 제외한 모든 순간을 로깅한다.

단순한 GET 요청도 단위 시간당 빈도수 등을 계산하여 이후 불량 유저 처리 등에 사용될 수 있으므로 로깅을 할 수 있을 것으로 생각한다.

 

비즈니스 목적이라면 유저의 모든 행위, 모든 실패, 모든 시도를 로깅하는 것이 가장 훌륭한 방법인 것같다.

유저의 정보와 모든 행위를 기록한다면 빅데이터로서 어떤 가치를 가지게 될테므로.

하지만 적절한 데이터 처리 분석 능력이 없다면 리소스 낭비일 것이므로 비즈니스 목적이라도 적절한 순간을 정해 로깅을 해야 한다고 생각한다.

 

레퍼런스

https://stackoverflow.com/questions/8601831/do-not-use-system-out-println-in-server-side-code

 

Do not use System.out.println in server side code

I heard that using System.out.println for logging purposes is a very bad practice and this may force the server to fail. I don't use this approach but I am very interested in knowing why System.out.

stackoverflow.com

https://meetup.toast.com/posts/149

 

(Spring Boot)Logging과 Profile 전략 : NHN Cloud Meetup

(Spring Boot)Logging과 Profile 전략

meetup.toast.com

https://blog.lulab.net/programmer/what-should-i-log-with-an-intention-method-and-level/

 

무엇을 로그로 작성할까? 로그 목적, 방법 그리고 로그 레벨

즐겁게 코딩을 하면서 즐기는 공간 '룰루랄라코딩'입니다. '무엇을 로그로 작성할까? 로그 목적, 방법 그리고 로그 레벨'을 한 번 살펴보세요.

blog.lulab.net

 

https://goddaehee.tistory.com/206

 

[스프링부트 (5)] Spring Boot 로그 설정(1) - Logback

[스프링부트 (5)] Spring Boot Log 설정(1) - Logback 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ 스프링 부트 Log 설정 - 로그백] 입니다. : -) 1. Logback 이란? 특징?  - 자바 오픈소스 로깅 프레임..

goddaehee.tistory.com

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함