Log4j 2 제대로 사용하기 - 설정

디디·2020년 7월 18일
8

Logging

목록 보기
1/1

보통 개발 과정에서 실행 시 에러가 나면, 콘솔의 에러 로그를 찾아서 디버깅하고 다시 실행하기 마련이죠. 하지만 배포 환경이라면 어떨까요? 만약 배포 환경에서 에러가 난다면 콘솔의 로그를 찾는 것 만으로는 한계가 있을 거에요. 이처럼, 배포 환경에서의 로그는 개발 환경과는 다른 방식으로 처리되어야 하겠죠. 이번 글에서는 이러한 로깅을 도와주는 Log4j2를 다뤄보려고 합니다.

사용법

의존성

plugins {
    id 'org.springframework.boot' version '2.3.1.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.fucct'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-log4j2'
    testImplementation 'org.springframework.boot:spring-boot-starter-log4j2'
    implementation 'org.apache.logging.log4j:log4j-web:2.12.1'
    implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.1'
    implementation 'com.lmax:disruptor:3.4.2'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

configurations {
    all {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
    }
}

test {
    useJUnitPlatform()
}

build.gradle

기본적으로 Spring은 Slf4j라는 로깅 프레임워크를 사용합니다. 구현체를 손쉽게 교체할 수 있도록 도와주는 프레임 워크입니다. Slf4j는 인터페이스고 내부 구현체로 logback을 가지고 있는데, 저는 Log4j 2를 사용하기 위해 exclude 했습니다.

설정 파일

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
 <!--    해당 설정파일에서 사용하는 프로퍼티-->
    <Properties>
        <Property name="logNm">Spring Log4j2 Test</Property>
        <Property name="layoutPattern">%style{%d{yyyy/MM/dd HH:mm:ss,SSS}}{cyan} %highlight{[%-5p]}{FATAL=bg_red, ERROR=red,
            INFO=green, DEBUG=blue}  [%C] %style{[%t]}{yellow}- %m%n -</Property>
    </Properties>
  <!--    LogEvent를 전달해주는 Appender-->
    <Appenders>
        <Console name="Console_Appender" target="SYSTEM_OUT">
            <PatternLayout pattern="${layoutPattern}"/>
        </Console>
        <RollingFile name="File_Appender" fileName="logs/${logNm}.log" filePattern="logs/${logNm}_%d{yyyy-MM-dd}_%i.log.gz">
            <PatternLayout pattern="${layoutPattern}"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="200KB"/>
                <TimeBasedTriggeringPolicy interval="1"/>
            </Policies>
            <DefaultRolloverStrategy max="10" fileIndex="min"/>
        </RollingFile>
    </Appenders>
  <!--    실제 Logger-->
    <Loggers>
        <Root level="INFO" additivity="false">
            <AppenderRef ref="Console_Appender"/>
            <AppenderRef ref="File_Appender"/>
        </Root>
        <Logger name="org.springframework" level="DEBUG"
                additivity="false">
            <AppenderRef ref="Console_Appender" />
            <AppenderRef ref="File_Appender"/>
        </Logger>
        <Logger name="com.fucct" level="INFO" additivity="false">
            <AppenderRef ref="Console_Appender" />
            <AppenderRef ref="File_Appender"/>
        </Logger>
        <Logger name="com.fucct.springlog4j2.loggertest" level="TRACE" additivity="false">
            <AppenderRef ref="Console_Appender" />
        </Logger>
    </Loggers>
</Configuration>

log4j2.xml

Log4j 2에선 설정파일을 Json, xml, yml등 다양한 방식으로 사용할 수 있습니다. 저는 jackson 의존성을 추가하고 싶지 않아서 xml을 사용했습니다. 해당 설정파일에 대해 알지 못해도 괜찮습니다. 다음 글에서 충분히 설명해드릴게요!😎
해당 설정을 log4j2.xml 로 지정하고, resources 패키지에 저장하시고, application.yml 파일에 해당 프로퍼티를 추가하시면 됩니다.

logging:
  config: classpath:log4j2.xml

application.yml

해당 설정을 완료하시면 로거를 자유롭게 사용하실 수 있습니다. 다만 설정을 어떻게 하는지는 다음 글에서 다뤄보도록 하겠습니다. 너무 많은 내용을 한 글에 담으면 지루하더라구요 😭

실제 사용

@RestController
@RequestMapping("/api/samples")
@RequiredArgsConstructor
@Slf4j
public class SampleController {
    private static final Logger logger = LogManager.getLogger(SampleController.class);

    @GetMapping
    public ResponseEntity<String> create(@RequestParam("name") String request) {
        logger.info("Hello. This is LogManager's logger");
        log.info("Hello. This is Lombok's logger");
        System.out.println(logger.equals(log));
        return ResponseEntity.ok(request);
    }
}

SampleController.java
(과연 출력문의 결과는 어떻게 될까요?)

해당 로깅의 결과는 다음과 같습니다.

컨트롤러의 첫 로거(logger)는 필드에 존재하는 로거입니다. 두 번째 로거(log)는 Lombok의 @Slf4j를 통해 받아온 로거입니다. 사실 같은 구현체를 쓰기 때문에 차이는 없습니다. 실제 Lombok이 컴파일된 코드는 다음과 같습니다.

테스트

참고로 테스트 코드에선 Lombok의존성과 Log4j2의존성을 테스트 스코프로 설정하지 않으면 사용할 수 없습니다. 물론 의존성을 추가하는 것도 좋지만 해당 방식으로 로거를 구해올 수 있습니다.

@SpringBootTest
public class LoggerTest {
    private static Logger logger = null;

    @BeforeEach
    public void setLogger() {
        System.setProperty("log4j.configurationFile", "log4j2.yml");
        logger = LogManager.getLogger();
    }

    @RepeatedTest(2)
    void givenLoggerWithDefaultConfig_whenLogToConsole_thanOK() {
        Exception e = new RuntimeException("Hello World!");

        logger.info("THIS IS INFO LEVEL LOG");
        logger.debug("THIS IS DEBUG LEVEL LOG");
        logger.error("THIS IS ERROR LEVEL LOG.", e);
        logger.fatal("THIS IS FATAL LEVEL LOG.");
    }
}

물론 단순한 테스트에도 SpringBootTest를 해야한다는 단점이 있어, 더 좋은 방법이 있다면 공유해드리도록 하겠습니다.

마치며

이렇게 간단한 Log4j2 설정방법과 사용법에 대해 공유해봤습니다. 물론 구체적인 내용을 말씀드리진 않았지만, 설정이 안되면 코딩을 하면서 공부하기가 어렵더라구요 🤔 코딩 하면서 공부하는 걸 좋아하는 저로썬, 설정 먼저 해서 직접 쳐보고, 그 내용을 바탕으로 확장시켜 나가는게 좋다고 생각합니다. 여기서 글을 마무리 하고, 다음 글에선 설정 파일의 의미를 설명드리고, 커스텀하는 방법에 대해 이야기 해볼게요. 긴 글 읽어주셔서 감사합니다😊

profile
식빵 고양이 디디

1개의 댓글

comment-user-thumbnail
2021년 3월 11일

정말 잘봤습니다....감사합니다.
다음편이 기대 됩니다.

답글 달기