
개발 초기: 콘솔 로그
데이터 영속: 파일 로그,
중앙 집중 관리 및 분석: AWS CloudWatch Logs
Spring Boot 환경에서 SLF4J와 Logback을 활용한 환경별(Profile) 설정, MDC를 통한 추적성 확보, 구조화된 로깅(Structured Logging) 등 실무적인 기법까지 포함하여 견고한 로깅 아키텍처 구축 방법 공부
INFO 레벨 이상의 콘솔 로그를 출력해 개발 편의성을 높임.TimeBasedRollingPolicy: 날짜(일, 시간) 기준으로 파일을 분리. (예: app-2023-10-27.log)SizeAndTimeBasedRollingPolicy: 날짜 기준에 추가로 파일 크기 제한을 두어, 특정 용량 초과 시 같은 날짜 안에서도 파일을 분리. (예: app-2023-10-27.0.log)애플리케이션 → 파일 로그 → CloudWatch Agent → CloudWatch Logslogback-spring.xml) - 환경별(Profile) 분리dev, local 환경에서는 콘솔로,
prod 환경에서는 파일로 로그를 남기도록 분리하는 것이 실무적.
Spring Boot의 'Profile' 기능을 활용함.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 공통 설정: 로그 파일 경로 및 패턴 정의 -->
<property name="LOG_PATH" value="./logs"/>
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId}] - %msg%n"/>
<!-- CONSOLE Appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- FILE Appender (Rolling Policy 적용) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/app.log</file>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/archived/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap> <!-- 전체 로그 최대 10GB 유지 -->
</rollingPolicy>
</appender>
<!-- 개발/로컬 환경: 콘솔에만 출력 -->
<springProfile name="dev, local">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<!-- 운영 환경: 파일에만 출력 (콘솔 출력은 성능 저하 유발 가능) -->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</springProfile>
</configuration>
config.json) - 타임스탬프 파싱로그 라인에 포함된 시간으로 CloudWatch 로그 시간을 설정해야 정확한 시간대별 조회가 가능함. timestamp_format 설정이 중요.
{
"agent": {
"run_as_user": "root"
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/home/ec2-user/app/logs/app.log",
"log_group_name": "my-prod-app-logs",
"log_stream_name": "{instance_id}",
"timezone": "Asia/Seoul",
"timestamp_format": "%Y-%m-%d %H:%M:%S.%f"
}
]
}
}
}
}
file_path: logback-spring.xml에 설정된 로그 파일의 절대 경로 지정.log_stream_name: {instance_id}로 설정 시, 각 EC2 인스턴스별로 로그 스트림이 자동 생성되어 서버별 로그 추적이 용이함.timestamp_format: LOG_PATTERN의 날짜/시간 형식과 정확히 일치시켜야 함.CloudWatchAgentServerPolicy 권한이 포함된 IAM Role이 연결되어 있어야 함.MDC(Mapped Diagnostic Context)를 사용하면 요청 단위로 고유 ID(예: traceId)를 부여하여, 특정 요청과 관련된 모든 로그를 쉽게 필터링할 수 있음.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
public class LogController {
private static final Logger logger = LoggerFactory.getLogger(LogController.class);
@GetMapping("/log")
public String generateLogs() {
// HTTP 요청 시작 시점에 Trace ID를 MDC에 추가
MDC.put("traceId", UUID.randomUUID().toString().substring(0, 8));
logger.info("사용자 요청 처리 시작.");
logger.warn("중요 파라미터 확인 필요.");
try {
// 다른 서비스 호출이나 복잡한 로직 수행
processSomething();
} catch (Exception e) {
logger.error("심각한 에러 발생! 원인 분석 필요.", e);
} finally {
// 요청 처리가 끝나면 MDC에서 Trace ID 제거
MDC.clear();
}
return "Logs generated and sent to CloudWatch!";
}
private void processSomething() {
logger.info("내부 로직 수행 중...");
if (System.currentTimeMillis() % 2 == 0) {
throw new RuntimeException("Simulated internal error");
}
}
}
Filter나 Interceptor를 사용하면 모든 요청에 대해 traceId를 자동으로 부여하고 제거하도록 개선할 수 있음.Structured Logging (JSON 형식 로깅): 로그를 key:value 형태의 JSON으로 구조화하는 기법. logstash-logback-encoder 라이브러리를 사용하면 쉽게 구현 가능.
json.level = 'ERROR' 와 같이 필드 기반의 정교한 쿼리가 가능해져 분석 효율이 극대화됨.logback-spring.xml 설정:<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/app.json.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
...
</appender>SLF4J (Simple Logging Facade for Java)의 본질:
Logger, LoggerFactory가 여기에 속함.CloudWatch Logs Insights 쿼리:
ERROR 로그 중, 특정 traceId를 가진 로그들을 시간 순으로 정렬하여 메시지와 예외 정보를 확인.fields @timestamp, @message, `exception.message`
| filter level = 'ERROR' and traceId = 'a1b2c3d4'
| sort @timestamp desc
| limit 20