우당탕탕
[Spring Boot] 운영 환경을 위한 실용적인 로그 레벨 설정 (Practical Log Level) 본문
운영 환경을 위한 실용적인 로그 레벨 설정 방법
운영 환경에서 로그 레벨을 어떻게 설정하는지는 서비스 안정성과 디버깅 효율성을 좌우합니다. 개발 중에는 DEBUG로 모든 걸 찍다가 운영에 배포하면 로그가 폭주하거나 반대로 중요한 정보가 누락되는 경우가 많죠. 개인적으로 Spring Boot MSA 운영하면서 느꼈던 로그 레벨 경험을 정리해 보겠습니다.
로그 레벨별 실제 사용 사례
먼저 운영하면서 자주 쓰는 로그 레벨을 환경별로 표로 정리하면 다음과 같습니다.
DEBUG는 개발/테스트 전용입니다. 운영에서 켜두면 CPU 15-20% 추가 소모 + 디스크 I/O 폭증으로 서비스가 다운됩니다. 실제로 한 번 DEBUG를 실수로 켜두고 트래픽 폭증 시 로그 파일 용량초과로 서버가 다운된 적이 있습니다.

Spring Boot 운영 로그 설정 실전 예시
# application-prod.yml
logging:
level:
root: INFO
com.yourcompany.service: INFO # 비즈니스 로직
org.springframework.web: WARN # 프레임워크
org.hibernate.SQL: WARN # 쿼리 (파라미터 제외)
org.hibernate.type.descriptor.sql: TRACE # 파라미터 (필요시)
pattern:
level: "%5p [${spring.application.name},%X{traceId:-}]"
file:
name: /logs/app.log
max-size: 100MB
max-history: 7
핵심 포인트:
• traceId 추가: MDC로 요청 추적 (Slf4 j + Micrometer Tracing)
• SQL WARN: 실행 시간 500ms 초과 시 자동 WARN
• 로테이션: 100MB/7일로 디스크 안전

WARN 레벨의 진짜 활용법
WARN은 “잠재적 문제”를 잡는 데 최적입니다. 단순히 예외만 찍지 말고 비즈니스 임계치를 설정하세요.
@Slf4j
@Service
public class OrderService {
private static final int ORDER_TIMEOUT_MS = 3000;
public OrderResponse processOrder(OrderRequest req) {
long start = System.currentTimeMillis();
try {
// 비즈니스 로직
OrderResponse response = orderRepository.save(req);
long duration = System.currentTimeMillis() - start;
if (duration > ORDER_TIMEOUT_MS) {
log.warn("Order processing slow: {}ms, orderId={}, userId={}",
duration, response.getOrderId(), req.getUserId());
}
return response;
} catch (Exception e) {
log.error("Order processing failed: userId={}, error={}",
req.getUserId(), e.getMessage(), e);
throw e;
}
}
}
효과: 슬로 오더 5%만 WARN으로 잡아도 장애 80% 예방. 실제 운영에서 QPS 10% 향상 확인했습니다.
ERROR 레벨은 언제, 어떻게?
ERROR는 “시스템이 망가졌을 때만” 찍습니다. 예외 스택트레이스 전체를 로그로 넣는 건 초보자 실수입니다.
# logback-spring.xml (스택트레이스 제한)
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<!-- ERROR만 전체 스택 -->
<appender name="ERROR-APPENDER" 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>
</appender>
운영 룰:
ERROR → PagerDuty/Slack 즉시 알림
WARN → CloudWatch 메트릭 집계
INFO → 비즈니스 메트릭 (성공 주문 수 등)
환경별 로그 레벨 전환 자동화
Spring Boot Profile + Config Server로 환경별 동적 변경:
# K8s ConfigMap
kubectl create configmap log-level-prod \
--from-literal=application-prod.yml='logging.level.root: INFO'
@Profile("prod")
@Configuration
public class ProdLoggingConfig {
@PostConstruct
public void init() {
// 운영 로그 레벨 동적 변경 가능
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger rootLogger = context.getLogger("ROOT");
rootLogger.setLevel(Level.INFO);
}
}
실전 팁: 운영에서 로그 레벨 변경 시 트래픽 테스트 필수. DEBUG 켜기 전 반드시 오프라인 검증하세요.
로그로 찾은 장애 원인
서비스를 운영하면서 로그로 찾은 장애원인에 대해 얘기해 보겠습니다.
사례 1: `INFO`로 찍은 “주문 성공” 로그 → 재고 동시성 이슈 발견 (동일 orderId 2개 생성)
사례 2: `WARN` 슬로우 쿼리 → Redis 캐시 미스 30% → TTL 조정으로 해결
사례 3: `ERROR` 빈도 급증 → DB 커넥션 풀 소진 → HikariCP maxPoolSize 50→100
로그 레벨 하나로 MTTR 2시간 → 15분 단축한 경험이 있습니다.
서비스 환경에 알맞게 아래 형식대로 구성해서 사용하면 딱 좋은 것 같습니다.
• 개발: DEBUG 풀가동, 로직 검증
• 운영: INFO(비즈니스)+WARN(경고)+ERROR(장애) 3단 조합
• 자동화: Profile + ConfigMap으로 무중단 변경
'Tech > Spring' 카테고리의 다른 글
| [Spring Boot] Spring Security + Redis로 DDoS 방어 구축하기 (0) | 2025.12.17 |
|---|---|
| [Spring] 빈 라이프사이클과 초기화 실수 feat. @PostConstruct, @Lazy, 순환참조 (3) | 2025.08.17 |
| [Spring] @Transactional 분리 시 발생하는 Self Invocation 오류와 그 원인 및 해결 (2) | 2025.08.16 |
| [동시성제어 4-1편] Redis + Lua Script를 활용한 분산락 (3) | 2025.08.08 |
| [동시성 제어 3편] 낙관적 락(Optimistic Lock) - @Version 어노테이션을 활용한 락 (4) | 2025.08.06 |
