로깅 프레임워크 초기 설정의 효율화
Java 기반의 신규 프로젝트를 시작할 때마다 로깅 프레임워크를 구성하고 의존성을 추가하는 작업은 반복적일 뿐만 아니라 설정 누락으로 인한 오류를 유발할 수 있습니다. SLF4J와 Logback 조합은 Java 생태계에서 가장 널리 사용되는 표준 로깅 솔루션으로, 이를 활용한 견고한 프로토타입을 신속하게 구축하는 방법을 살펴봅니다.
Maven 빌드 구성 및 의존성 관리
프로젝트의 pom.xml 파일에 필요한 라이브러리를 선언합니다. SLF4J는 로깅 파사드(Facade) 역할을 하며, Logback은 실제 로깅 구현체로 동작합니다. 또한 테스트 검증을 위해 JUnit 5와 보일러플레이트 코드를 줄이기 위한 Lombok을 포함합니다.
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<slf4j.version>2.0.9</slf4j.version>
<logback.version>1.4.11</logback.version>
<junit.jupiter.version>5.10.0</junit.jupiter.version>
<lombok.version>1.18.30</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Logback 설정 파일 작성
src/main/resources 디렉터리에 logback.xml 파일을 생성하여 로그의 출력 형식, 레벨, 대상을 정의합니다. 개발 환경에서의 실시간 확인을 위한 콘솔 앱엔더와 운영 환경에서의 지속적인 저장을 위한 롤링 파일 앱엔더를 구성합니다.
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="ROLLING_FILE" />
</root>
</configuration>
비즈니스 로직 및 로거 구현
Lombok의 @Slf4j 애너테이션을 활용하여 로거 인스턴스 선언을 자동화합니다. 또한 MDC(Mapped Diagnostic Context)를 적용하여 트랜잭션 식별자를 로그에 포함시킴으로써 추후 로그 추적성을 높입니다.
package com.example.logging;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
@Slf4j
public class PaymentService {
public void executePayment(String transactionId, double amount) {
MDC.put("txId", transactionId);
log.info("Initiating payment process for amount: {}", amount);
try {
validateAmount(amount);
log.debug("Payment validation passed for transaction: {}", transactionId);
// 실제 결제 처리 로직
} catch (IllegalArgumentException e) {
log.error("Payment validation failed for txId {}: {}", transactionId, e.getMessage());
} catch (Exception e) {
log.error("Unexpected error during payment processing for txId {}", transactionId, e);
} finally {
MDC.remove("txId");
}
}
private void validateAmount(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Payment amount must be greater than zero.");
}
}
}
JUnit을 활용한 로깅 동작 검증
단위 테스트를 통해 비즈니스 로직이 의도한 대로 실행되며, 예외 상황에서 에러 로그가 적절히 생성되는지 확인합니다.
package com.example.logging;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
class PaymentServiceTest {
private final PaymentService paymentService = new PaymentService();
@Test
void shouldProcessValidPayment() {
assertDoesNotThrow(() -> paymentService.executePayment("TX-98765", 150.00));
}
@Test
void shouldThrowExceptionForInvalidAmount() {
assertThrows(IllegalArgumentException.class, () -> {
paymentService.executePayment("TX-11111", -50.00);
});
}
}
장애 대응 및 아키텍처 확장
로거 초기화 단계에서 NoClassDefFoundError 또는 ClassNotFoundException이 발생한다면, SLF4J 바인딩 라이브러리(Logback)가 런타임 클래스패스에 제대로 포함되지 않았는지 확인해야 합니다. 또한, 분산 환경에서는 MDC에 Correlation ID를 주입하고, Logstash나 Fluentd와 같은 로그 수집 에이전트와 연동하여 중앙 집중형 로그 관리 시스템을 구축하는 것이 권장됩니다.