스프링부트/Logging

[Spring]로그란? - 1.Log4j2 설정

삼록이 2025. 7. 15. 23:34

1.로그란?

시스템 동작시 시스템의 상태와 작동에 대한 정보에 대한 "기록"이다.

우리가 흔히 자바에서 어떠한 기록을 남기기 위해 혹은 어떤 정보를 얻기위해  System.print.ln()을 사용하여 터미널 창에 기록을 남기는 것도 로그를 남기는 것이다. 하지만 단순히 남기기만 하지않고 이 로그들을 어딘가에 저장해두고 이 로그를 통해 통계를 내거나 사용자의 기록을 추적하거나 의미있는 활동들을 한다. 따라서 이 로그를 더 활용하기 쉽게 하기 위해 다양한 기능이 추가된 라이브러리가 필요하다.

 

2.로그 라이브러리

0.log4j

초기에 개발된 Java 로깅 프레임워크 중 하나다. 가장 많이 사용되었지만 현재는 지원이 종료된 라이브러리로 사용이 권장되지 않는다.

 

1.logback

log4j와 유사하지만 향상된 성능과 필터링 옵션이 지원된다.

스프링 부트에서 spring-boot-starter-web에 안에 spring-boot-starter-logging 라이브러리가 포함되어있고 그 안에 이 logback이 있다. 그래서 우리가 흔히 기본적으로 사용한다. logback은 slf4j의 구현체다.

 

2.log4j2

logback 이후에 나온 프레임워크. logback과 유사하나 멀티스레드 환경에서 비동기 처리가 빠르다는 장점이 있다. 스프링 부트에서 log4j2를 사용하려면 spring-boot-starter-web으로 인해 내장되어 있는 logback을 제거하는 작업이 필요하다. log4j2도 slf4j의 구현체다.

나는 이후에, ELK를 통해 로그를 관리하려고 한다. 그러면 ELK에 로그를 전송해야하는데 그 과정에서 비동기 처리가 빠른 장점이 있는 log4j2를 사용하려 한다.

 

*Slf4j(Simple Logging Facade for Java)

다양한 로깅 라이브러리를 관할하는 인터페이스로 제공하는 라이브러리다. 이를 통해, 구현체 간 전환이 자유롭다.

https://programming-tree.tistory.com/116

 

 *ELK

Elasticsearch, Logstash, Kibana의 앞 글자를 딴 것으로 이 세가지 오픈소스를 묶어 부르는 말이다.

  • Elasticsearch는 로그 데이터를 저장하고, 빠르게 검색할 수 있는 '검색엔진'이라고 보면 된다.
  • Logstash는 다양한 형태의 로그를 스프링 서버로부터 받아서 가공하고 Elasticsearch로 전송하는 '로그 수집기'다.
  • Kibana는 Elasticsearch에 저장된 로그를 시각화하고 분석할 수 있는 '웹 대시보드' 도구이다.

3.Log4j2 사용 설정

1. build.gradle에서 configurations 단락이 있을 것이다. 거기에 configureEach{exclude~} 이 부분을 복붙해준다.

spring-boot-starter에 기본 내장되어 있는 logback이 포함되어있는 기본 로깅 라이브러리를 제거하겠다는 의미다.

그래야 log4j2와 충돌이 나질 않는다.

 

*대부분의 블로그들은 configureEach대신 all을 사용하는데 최신 그래들에서는 all대신 configureEach를 사용하는 것이 권장된다고 한다.

configurations {
    compileOnly {
       extendsFrom annotationProcessor
    }
    configureEach{
     exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
    }
}

 

2.dependencies{}에 아래 log4j2 의존성을 추가한다.

//log4j2
implementation 'org.springframework.boot:spring-boot-starter-log4j2'

 

3.resources 폴더 아래에 log4j2-spring.xml파일을 생성한다.

*보통은 log4j2.xml파일로 이름을 명명하나, -spring을 붙이면 내부설정에서 내가 active 한 profile에 따른 개별 설정이 가능하다는 장점이 있다.

4.아래와 같이 log4j2-spring.xml 파일을 세팅한다.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
    <Properties>
        <Property name="LOG_PATH">logs</Property>
        <Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss} [%t] %highlight{%-5level} %logger{36} - %msg%n</Property>
    </Properties>

    <Appenders>
        <!-- 콘솔 로그 -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout disableAnsi="false" pattern="${LOG_PATTERN}" />
        </Console>

        <!-- 파일 로그 -->
        <SpringProfile name="prod">
            <RollingFile name="File" fileName="${LOG_PATH}/app.log"
                         filePattern="${LOG_PATH}/app-%d{yyyy-MM-dd}.log.gz">
                <PatternLayout pattern="${LOG_PATTERN}" />
                <Policies>
                    <TimeBasedTriggeringPolicy interval="1" modulate="true" />
                </Policies>
                <DefaultRolloverStrategy>
                    <Delete basePath="${LOG_PATH}">
                        <IfAccumulatedFileCount exceeds="30"/>
                    </Delete>
                </DefaultRolloverStrategy>
        </RollingFile>
        </SpringProfile>
    </Appenders>

    <Loggers>
        <!-- 패키지별 로그 레벨 설정 -->
        <Logger name="org.springframework" level="INFO" additivity="false">
            <AppenderRef ref="Console" />
        </Logger>

        <!-- 전체 애플리케이션 기본 로그 -->
        <Root level="DEBUG">
            <AppenderRef ref="Console" />
            <SpringProfile name="prod"> 
                <AppenderRef ref="File" />
            </SpringProfile>
        </Root>
    </Loggers>
</Configuration>

 

*설정 파일 해석

<Configuration status="WARN" monitorInterval="30">

status = " WARN" : log4j2 자체의 내부 로깅 레벨이 WARN 이상일 때만 출력한다는 의미

monitorInterval = "30" : 설정 파일을 30초마다 자동 감지해서 변경사항을 반영한다는 의미다. log4j2는 설정파일을 자동으로 감지하는 핫리로딩 이라는 기능이 있다. 이 기능으로 인해 서버를 재시작하지 않아도 설정파일이 변경된 걸 감지하여 반영할 수 있다.

설정한 후 변경할 이유가 없으므로 사실 WARN까지만 작성해서 핫리로딩 기능을 꺼도 된다.

<Properties>
    <Property name="LOG_PATH">logs</Property>
    <Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss} [%t] %highlight{%-5level} %logger{36} - %msg%n</Property>
</Properties>

Properties는 이 설정파일에서 자주 쓰이는 값을 변수처럼 정의하는 블록이다.

${LOG_PATH}라는 이름으로 logs 문자열을 저장한다는 의미다. 이것은 아래에 나올 로그 파일이 저장될 경로(폴더이름)으로 사용하기 위해  작성되었다.

LOG_PATTERN은  로그 메시지 출력 형식을 지정하는 것이다.

 

  • %d: 날짜 (yyyy-MM-dd HH:mm:ss.SSS)
  • %t: 쓰레드 이름
  • %highlight :로그 레벨별로 색상입히기(log4j2는 기본적으로 모든 레벨이 흰색처리되어 이 속성을 붙이는게 편하다. 다만, disableAnsi="false" 설정도 PatternLayout을 명시하는 쪽에서 같이 해주어야 로그 레벨별로색상이 입혀진다) 
  • %-5level: 로그 레벨 (INFO, ERROR 등)
  • %logger{36}: 로거 이름 (클래스명, 최대 36자)
  • %msg: 로그 메시지
  • %n: 줄바꿈

 

<Appenders>
    <!-- 콘솔 로그 -->
    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout disableAnsi="false" pattern="${LOG_PATTERN}" />
    </Console>

<Appenders> 블록은 로그를 어디로 출력할 지 정의하는 블록이다.

콘솔(터미널) 창 으로 출력하는 로그에 대한 설정이다. 위에서 정의한 LOG-PATTERN 형식으로 출력한다

 

<!-- 파일 로그 -->
    <SpringProfile name="prod">
        <RollingFile name="File" fileName="${LOG_PATH}/app.log"
                     filePattern="${LOG_PATH}/app-%d{yyyy-MM-dd}.log.gz">
            <PatternLayout pattern="${LOG_PATTERN}" />
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
            </Policies>
            <DefaultRolloverStrategy>
                <Delete basePath="${LOG_PATH}">
                    <IfAccumulatedFileCount exceeds="30"/>
                </Delete>
            </DefaultRolloverStrategy>
    </RollingFile>
    </SpringProfile>
</Appenders>

여기는 파일에 출력하는 로그에 대한 설정이다. 이 설정으로 인해 로그파일이 생성되어 그 파일에도 로그기록이 남는다.

다만, 나는 굳이 로컬 환경에서는 파일 생성이 필요없어서 <SpringProfile name="prod"> 를 앞에 명시했다.

이 말은 application-prod.yml이 활성화 되었을 때만 파일로그 블록 설정이 적용된다는 말이다.

이것이 아까 언급했듯 log4j.xml파일이라 이름을 짓지 않고 log4j2-spring.xml이라 이름 지은 이유다.

(log4j2-spring.xml로 작성해야 <SpringProfile> 기능을 쓸 수 있다)

 

그리고 <RollingFile>~은 날짜별로 롤링되어 파일이 생성된다는 의미다.

<TimeBasedTriggeringPolicy> 에서 하루에 한번. modulata=true로 딱 자정을 기준으로 롤링되어 파일이 생기도록 설정했다.

그리고 <Delete>블록을 통해 생성된지 로그 파일의 개수가 30개가 초과하면 자동 삭제하도록 설정했다.

이 말은 정상적인 실행환경에서는  30일이 지난 로그파일은 자동삭제된다는 말과 동일하다고 봐도 무방하다.

 <Loggers>
        <!-- 패키지별 로그 레벨 설정 -->
        <Logger name="org.springframework" level="INFO" additivity="false">
            <AppenderRef ref="Console" />
        </Logger>

        <!-- 전체 애플리케이션 기본 로그 -->
        <Root level="INFO">
            <AppenderRef ref="Console" />
            <SpringProfile name="prod">
                <AppenderRef ref="File" />
            </SpringProfile>
        </Root>
    </Loggers>
</Configuration>

<Loggers>블록은 어느 패키지/클래스에 대해 어떻게 출력할 지 설정하는 영역이다.

org.springframework패키지에 속한 클래스들에 대해서는 INFO이상의 로그만 출력되도록 한다.

그리고 그 외 전체 스프링 애플리케이션에 관련한 로그에 대해서도 INFO이상의 로그만 출력되도록 설정했다.

파일에 출력하는 것은 어차피 prod환경에서만 작동되도록 해놨으므로 여기서도 SpringProfile name="prod"로 감싼다.