개발 가이드 공통 (Common) 로깅 규칙
최종 수정:

로깅 규칙

SLF4J + Logback 기반 로깅 규칙 및 Slack 연동

로깅 규칙

기본 원칙

  • System.out.println 절대 금지 → @Slf4j 사용
  • 문자열 + 연산 금지 → {} 바인딩 사용 (지연 평가로 성능 향상)
  • 로그에 개인정보 (이름, 이메일, 전화번호), 비밀번호, 토큰 출력 금지
  • 운영 환경에서 DEBUG 비활성화
  • ERROR 레벨 발생 시 Slack 알람 자동 발송 (GlobalExceptionHandler 연동)

로거 선언

// Lombok @Slf4j 어노테이션 사용 (권장)
@Slf4j
@Service
public class NewsService {
    // log.info(), log.warn(), log.error() 바로 사용
}

// 직접 선언 (Lombok 미사용 시)
private static final Logger log = LoggerFactory.getLogger(NewsService.class);

로그 레벨 기준

레벨사용 상황예시
ERROR장애, 복구 불가능한 오류DB 연결 실패, 외부 API 오류
WARN주의, 비정상이지만 계속 가능재시도, 예상치 못한 상태
INFO주요 비즈니스 흐름배치 시작/완료, 수집 결과
DEBUG개발용 상세 정보파라미터 값, 중간 계산

올바른 로깅 예시

// INFO — 주요 비즈니스 이벤트
log.info("다이제스트 배치 시작");
log.info("뉴스 수집 완료: source={}, count={}", source, count);
log.info("다이제스트 메일 발송 완료: to={}", mailTo);

// WARN — 비정상이지만 계속 가능한 상황
log.warn("뉴스 수집 일부 실패: source={}, error={}", source, e.getMessage());
log.warn("외부 API 응답 지연: url={}, elapsed={}ms", url, elapsed);
log.warn("Slack 알림 전송 실패: {}", e.getMessage());

// ERROR — 스택트레이스 포함 (Slack 알람 트리거)
log.error("DB 연결 실패: datasource={}", datasource, exception);
log.error("뉴스 수집 실패: source={}", source, e);

// DEBUG — 개발 환경에서만 출력
log.debug("쿼리 파라미터: keyword={}, page={}", keyword, page);

잘못된 로깅 (금지)

// 개인정보 출력 금지
log.info("사용자: email={}, phone={}", email, phone);   // 금지
log.info("로그인: password={}", password);               // 절대 금지
log.info("API 키: key={}", apiKey);                      // 절대 금지

// 문자열 연결 금지 (성능 저하)
log.info("처리 완료: " + count + "건");                   // 금지

// System.out.println 금지
System.out.println("배치 시작");                          // 금지

// 스택트레이스를 응답에 노출 금지 (로그에만)
return ResponseEntity.status(500).body(e.getMessage()); // 스택트레이스 노출 가능성

Slack 알람 연동

GlobalExceptionHandler에서 500 에러 발생 시 자동으로 Slack 알람이 발송된다.

// GlobalExceptionHandler 내부
slackService.sendError(request.getRequestURI(), e);

배치 실패 시 직접 호출:

@Slf4j
@Component
@RequiredArgsConstructor
public class DigestScheduler {

    private final SlackService slackService;

    @Scheduled(cron = "0 0 7 * * *")
    public void runDigest() {
        try {
            digestOrchestrator.runDigest();
            log.info("다이제스트 배치 완료");
        } catch (Exception e) {
            log.error("다이제스트 배치 실패", e);
            slackService.sendBatchFail("DigestScheduler", e);
        }
    }
}

Logback 설정 (logback-spring.xml)

<!-- src/main/resources/logback-spring.xml -->
<configuration>

    <springProfile name="!prod">
        <!-- 개발 환경: 콘솔 출력 -->
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>

        <root level="DEBUG">
            <appender-ref ref="CONSOLE"/>
        </root>

        <logger name="com.scraping.agent" level="DEBUG"/>
        <logger name="org.hibernate.SQL" level="DEBUG"/>
    </springProfile>

    <springProfile name="prod">
        <!-- 운영 환경: 파일 출력 (rolling) -->
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>/var/log/agent/app.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>/var/log/agent/app.%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="FILE"/>
        </root>

        <logger name="com.scraping.agent" level="INFO"/>
        <!-- 운영 환경: SQL 쿼리 로그 비활성화 -->
        <logger name="org.hibernate.SQL" level="WARN"/>
    </springProfile>

</configuration>

application.yml 로깅 설정

logging:
  level:
    root: INFO
    com.scraping.agent: INFO
    org.hibernate.SQL: ${SQL_LOG_LEVEL:WARN}   # 개발: DEBUG, 운영: WARN
    org.springframework.security: WARN

보안 체크리스트

  • [ ] 로그에 개인정보(이름, 이메일, 전화번호) 미포함
  • [ ] 로그에 비밀번호, 토큰, API 키 미포함
  • [ ] 운영 환경 DEBUG 로그 비활성화
  • [ ] 예외 로그: 스택트레이스는 로그에만, 응답에는 일반 메시지
  • [ ] System.out.println 사용 없음
  • [ ] 문자열 + 연결 없음 → {} 바인딩 사용
AI 문서 검색

현재 페이지 내용을 기반으로 질문하세요.