실시간 로그 수집 · 이상 탐지 · AI 자동복구 파이프라인
Spring Boot 애플리케이션의 로그를 ELK로 구조화하고, Z-score + 룰베이스 Hybrid 방식으로 이상을 탐지해 AI Agent가 자율 복구하는 시스템
금융 결제 API에서 발생하는 장애를 사람 개입 없이 자동으로 감지하고 복구하는 시스템입니다.
로그 수집부터 이상 탐지, AI Agent 자동복구, 판단 이력 저장까지 전체 파이프라인을 구현했습니다.
| 분류 | 기술 |
|---|---|
| Backend | Spring Boot 3.3, Java 21 |
| AI Agent | LangChain4j 0.35.0, GPT-4o-mini (ReAct 패턴) |
| 로그 수집 | Logstash 8.12 |
| 검색·탐지 | Elasticsearch 8.12 |
| 시각화 | Kibana 8.12, Streamlit |
| DB | PostgreSQL 16 |
| 인프라 | Docker Compose |
Streamlit Dashboard
↕ REST API
Spring Boot (port 8080)
│
├─ MDC 기반 JSON 로그
│ ↓
│ Logstash (파싱·변환)
│ ↓
│ Elasticsearch (인덱싱)
│ ↓
│ AnomalyDetectionService (30초 주기 Z-score 탐지)
│ ↓ 이상 감지 시
└─ AI Agent (LangChain4j ReAct)
├─ GetMetrics → 실시간 지표 조회
├─ SearchLogs → ES 에러 로그 검색
├─ AnalyzePattern → 실패 패턴 분석
├─ RestartService → 오류 모드 해제 + 실패 결제 재시도
├─ SendAlert → 알림 DB 저장
└─ GenerateReport → 장애 리포트 생성
│
PostgreSQL (판단 이력 영구 저장)
finguard/
├── api-pulse-app/ # Spring Boot 백엔드
│ └── src/main/java/com/apipulse/api_pulse_app/
│ ├── agent/ # AI Agent
│ │ ├── FinGuardAgent.java # GPT-4o-mini 인터페이스
│ │ ├── AgentExecutionService.java # Agent 실행 + 로그 저장
│ │ ├── EscalationPolicyService.java# 에스컬레이션 판단
│ │ └── tools/ # Tool 6개
│ ├── monitoring/ # 이상 탐지
│ │ ├── AnomalyDetectionService.java# Z-score Hybrid 탐지
│ │ └── MonitoringScheduler.java # 30초 스케줄러
│ ├── alert/ # 알림 관리
│ ├── payment/ # 결제 도메인
│ └── filter/
│ └── LoggingFilter.java # MDC 기반 자동 로깅
├── elk/
│ └── logstash/pipeline/
│ └── api-pulse.conf # is_error 분류 파이프라인
├── streamlit/ # 대시보드
│ ├── app.py # 홈 — 알림배너 / 지표카드
│ └── pages/
│ ├── 1_모니터링.py
│ ├── 2_챗봇.py
│ └── 3_Agent_이력.py
├── scenario/ # 시나리오 스크립트
└── docker-compose.yml
비정형 로그는 집계도, 이상 탐지도 불가능합니다. OncePerRequestFilter로 모든 API 요청에 자동 로깅을 적용해 개발자가 컨트롤러마다 로깅 코드를 작성하지 않아도 전 구간이 traceId 단위로 추적됩니다.
MDC.put("traceId", UUID.randomUUID().toString().replace("-", "").substring(0, 8));
MDC.put("method", request.getMethod());
MDC.put("uri", request.getRequestURI());
MDC.put("statusCode", String.valueOf(response.getStatus()));
MDC.put("responseTime", String.valueOf(duration));Spring Boot가 출력하는 JSON 로그를 Logstash가 파싱·변환해 Elasticsearch에 인덱싱합니다.
HTTP 상태코드가 200이어도 애플리케이션 레벨에서 ERROR가 발생하는 경우를 처리하기 위해 level == "ERROR" 조건을 추가해 정확한 에러 분류를 구현했습니다.
# level이 ERROR이거나 statusCode가 500 이상이면 is_error: true
if [level] == "ERROR" or ([statusCode] and [statusCode] >= 500) {
mutate { add_field => { "is_error" => "true" } }
} else {
mutate { add_field => { "is_error" => "false" } }
}저장 구조:
- 인덱스:
api-pulse-YYYY.MM.dd(일별) - 주요 필드:
traceId,level,is_error,statusCode,responseTime,uri
단순 룰베이스만 사용하면 트래픽이 적은 시간대에 1건 실패만으로도 에러율 100% 오탐이 발생합니다. 데이터 분포를 고려한 Z-score를 결합해 맥락 기반 탐지를 구현했습니다.
// ES에서 최근 10분 데이터를 1분 버킷으로 집계
double zScore = std < 0.001 ? 0.0 : (current - mean) / std;
boolean statisticalAnomaly = zScore > 2.0; // 평균 대비 2 표준편차 이탈
boolean criticalByRule = current >= 80.0; // 절대값 80% 이상 (OR 구조)
// 상태 기반 구조 — 동일 장애에서 Agent 중복 실행 방지
if (shouldTriggerAgent(currentStatus, nextStatus)) {
agentController.runAgent("STATISTICAL_ANOMALY", situation);
}
currentStatus = nextStatus;| 상태 | 조건 | 처리 |
|---|---|---|
| NORMAL | z < 2.0, 에러율 정상 | 대기 |
| WARNING | z > 2.0 | Agent 자율 처리 |
| CRITICAL | 에러율 >= 80% | 에스컬레이션 알림 |
SystemMessage에 순서를 고정하지 않고 판단 기준만 제시해 GPT가 상황에 따라 툴을 자율 선택하도록 설계했습니다.
@SystemMessage("""
당신은 금융 결제 시스템의 AI 장애 대응 에이전트입니다.
사용 가능한 툴을 활용해 상황을 스스로 판단하고 최적의 순서로 대응하세요.
각 툴 실행 결과를 바탕으로 다음 행동을 결정하세요.
복구가 불가능하다고 판단되면 즉시 에스컬레이션 알림을 발송하세요.
""")
String analyze(@UserMessage String situation);카드 오류 장애 발생 시 SearchLogs·AnalyzePattern을 생략하고 바로 RestartService를 호출한 실제 로그 — 순서 고정 없이 GPT가 스스로 불필요한 툴을 건너뛴 ReAct 동작의 증거입니다.
Agent 실행 완료 - 12084ms
사용 툴: GetMetrics, RestartService, SendAlert, GenerateReport
단순히 오류 모드를 끄는 것에서 나아가, 장애 중 발생한 실패 결제를 최대 3건까지 자동으로 재시도합니다.
@Tool("카드 오류로 인한 결제 장애를 자동 복구합니다.")
public String recoverFromCardError() {
paymentService.setCardErrorMode(false);
PaymentService.RetryResult result = paymentService.retryFailedPayments();
return String.format("자동복구 완료 - 카드 오류 모드 해제, 재시도: %s", result.message());
}① 카드 오류 ON → 결제 요청 실패
② MDC 필터가 ERROR 레벨 로그 자동 생성
③ Logstash가 is_error: true로 분류 → ES 저장
④ AnomalyDetectionService (30초 주기)
→ ES에서 최근 10분 집계
→ Z-score 계산: 3.16 > 2.0 → CRITICAL 판정
→ 상태 전환 감지 → Agent 트리거
⑤ FinGuardAgent.analyze() 호출
→ GPT가 상황 판단 → 툴 자율 선택
→ RestartService: 오류 모드 해제 + 실패 결제 재시도
→ SendAlert: 에스컬레이션 알림 생성
→ GenerateReport: 장애 리포트 저장
⑥ Streamlit 대시보드: NORMAL 복구 확인
⑦ 사람이 에스컬레이션 알림 읽음 처리
문제 새벽 시간대에 트래픽 2건 중 1건 실패 시 에러율 50%로 오탐 발생. 데이터 맥락 없이 숫자만 비교하는 방식의 한계.
해결 Z-score 도입으로 현재 값이 과거 평균에서 얼마나 이탈했는지 기준으로 판단 변경. 절대값 80% 조건을 보완적으로 유지해 Hybrid 구조로 전환.
문제 장애가 지속되는 동안 30초마다 Agent가 계속 실행되어 로그와 알림이 과도하게 쌓임. Agent가 이전 상태를 기억하지 못하는 stateless 구조가 원인.
해결 NORMAL / WARNING / CRITICAL 상태를 명시적으로 관리하는 state 기반 로직 도입. 이상 진입 시 1회만 실행, 상태 유지 중 재실행 차단, 정상 복구 시 자동 초기화.
private boolean shouldTriggerAgent(DetectionStatus current, DetectionStatus next) {
return current == DetectionStatus.NORMAL && next != DetectionStatus.NORMAL;
}문제 에러율이 120%처럼 100%를 초과하는 값 발생. 분자(ES 로그 기준)와 분모(DB 기준)가 서로 다른 데이터 소스를 참조하다 집계 시점 불일치로 발생.
해결 단일 데이터 소스 기준으로 통일. 방어 로직 추가.
double safeErrorVal = Math.min(errorVal, totalVal);
double rate = Math.min(safeErrorVal / totalVal * 100.0, 100.0);문제 AnomalyDetectionService에서 ES 쿼리 시 406 에러 발생. RestTemplate의 기본 Content-Type이 text/plain으로 전송되어 ES가 거부.
해결 restTemplate.exchange()로 변경하고 Content-Type: application/json 헤더 명시적 추가.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(query, headers);
ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.POST, entity, Map.class);문제 Logstash에서 statusCode >= 500일 때만 is_error: true로 설정했는데, 결제 실패 응답이 200으로 반환되어 모든 로그가 is_error: false로 저장됨.
해결 Logstash 파이프라인 조건에 level == "ERROR" 추가.
if [level] == "ERROR" or ([statusCode] and [statusCode] >= 500) {
mutate { add_field => { "is_error" => "true" } }
}- MDC 기반 자동 로깅으로 개발자 로깅 코드 없이 전 구간 traceId 추적
- Z-score Hybrid 탐지로 룰베이스 단독 대비 오탐률 감소
- 상태 기반 구조로 동일 장애에서 Agent 중복 실행 방지
- Agent가 상황에 따라 툴 자율 선택 (ReAct 패턴) — 불필요한 툴 호출 제거
- 장애 감지 → 자동복구 → 실패 결제 재시도까지 전체 파이프라인 자동화
- 모든 Agent 판단 이력 (입력값·사용 툴·결과·응답시간) PostgreSQL 영구 저장
- Docker Desktop 실행
- Java 21
- Python 3.9+
- OpenAI API Key
# api-pulse-app/src/main/resources/application.yml
openai.api-key: your_openai_api_key# 1. 인프라 실행
docker compose up -d
# 2. Spring Boot 실행
cd api-pulse-app
./gradlew bootRun
# 3. Streamlit 실행
cd streamlit
pip install -r requirements.txt
streamlit run app.py| 서비스 | URL |
|---|---|
| Spring Boot API | http://localhost:8080 |
| Streamlit 대시보드 | http://localhost:8501 |
| Kibana | http://localhost:5601 |
POST /payment/approve # 결제 승인
GET /payment/history # 결제 내역
POST /scenario/card-error/on # 카드 오류 시나리오 ON
POST /scenario/card-error/off # 카드 오류 시나리오 OFF
POST /scenario/payment-timeout/on
POST /scenario/payment-timeout/off
POST /agent/analyze/card-error # Agent 수동 실행
GET /agent/logs # Agent 실행 이력
POST /monitor/check # 모니터링 수동 체크
GET /alerts/unread # 미읽 알림 조회
POST /alerts/read-all # 전체 읽음 처리
- 아키텍처 — RabbitMQ 기반 비동기 처리 / Agent 서버 분리 (Spring → Flask)
- AI Agent — ReAct Thought/Action/Observation 단계별 로그 저장 / ML 기반 이상 탐지 확장
- 관측성 — Redis 캐싱으로 ES 조회 부하 감소 / 알림 정책 고도화
- He et al. (2017). Drain: An online log parsing approach with fixed depth tree. ICWS. https://doi.org/10.1109/ICWS.2017.13
- Goldstein & Uchida. (2020). Anomaly Detection: A Survey. arXiv. https://arxiv.org/abs/2004.00433
- LangChain4j 0.35.0 Documentation. https://docs.langchain4j.dev
- Elasticsearch 8.12 Documentation. https://elastic.co/docs
- 과학기술정보통신부. (2025). AI 기본법 제34조. https://www.law.go.kr