| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- EC2
- SISS
- S3
- 자료구조
- beebox
- acc
- CodeEngn
- 백준
- Linux
- mount
- pwnable
- datastructure
- AWS
- wireshark
- 유석종교수님
- fork-bomb
- Reversing
- Systemhacking
- Upstage
- System
- Dreamhack
- c
- bWAPP
- htmlinjection
- Reflected
- python
- basicrce3
- backjoon
- cloud
- 와이어샤크
- Today
- Total
Ctrl + Shift + ESC
Upstage Solar API를 이용해 자동으로 에러 로그 요약하기 본문
문제 정의
현재 저희 시스템의 로그 수집 구조는 다음과 같습니다.
EC2 로그
→ Promtail
→ Loki (로그 저장)
→ Grafana (조회, 알림, 시각화)
Promtail은 로그 파일이 존재하는 서버에 설치되어 로그를 수집하고, 이를 Loki로 전송합니다.
Loki는 Promtail로부터 전달받은 로그를 저장하고, Grafana로부터 들어오는 로그 쿼리 요청에 대해 결과를 반환합니다.
개발자는 Grafana를 통해 로그를 조회하거나, 특정 조건에 대한 알림을 설정할 수 있습니다.

문제는 로그의 양입니다.
실제 운영 환경에서는 Loki뿐 아니라 CloudWatch Agent 등 다양한 방식으로 로그를 수집하고 있습니다. 하지만 이렇게 수집된 로그들은 분석되지 않으면 그저 쌓이기만 하는 데이터에 불과합니다.
에러 로그는 여러 서버와 여러 서비스에 흩어져 있고, 하루에도 몇 초 간격으로 새로운 로그가 쌓입니다.
모든 로그를 사람이 일일이 확인하며 모든 에러를 파악하고 대응하기에는 시간과 인력이 절대적으로 부족합니다.
결국 중요한 로그를 놓치거나, 문제를 인지하는 시점이 늦어질 수밖에 없습니다.
해결 방안
그래서 저희는 이 문제를 해결하기 위해 LLM을 활용해 로그를 분석하는 봇을 만들기로 했습니다.
수많은 로그 중에서 의미 있는 로그를 선별하고, 중요한 에러를 요약·분석해 개발자가 빠르게 대응할 수 있도록 돕는 것이 목표입니다.

전체 시스템은 로그 수집 → 스케줄링 → 분석 및 요약 → 결과 전달의 흐름으로 동작합니다.
1. 로그 수집
서버(예: EC2 인스턴스)에서 발생하는 Spring Boot 로그를 Promtail을 통해 Grafana Loki로 전달합니다.
Promtail은 로그 파일이 존재하는 서버에서 로그를 수집하고, Loki는 이를 저장 및 조회 가능한 형태로 관리합니다.
현재 구조에서는 Promtail + Loki + Grafana 조합을 사용하고 있지만, 핵심은 로그가 S3 또는 조회 가능한 저장소에 적재되어 있다는 것입니다.
즉, S3에 로그를 저장하는 구조라면 동일한 방식으로 확장해 적용할 수 있습니다.
2. 스케줄링
Amazon EventBridge를 사용해 정해진 주기(KST 기준)에 분석 작업을 자동으로 트리거합니다.
예를 들어 매일 자정이나 업무 시작 전에 실행되도록 설정할 수 있으며, 사람이 직접 실행하지 않아도 주기적으로 로그 요약 리포트를 생성할 수 있습니다.
3. 분석 및 요약
EventBridge에 의해 호출된 AWS Lambda 함수는 다음 작업을 수행합니다.
- Loki에 로그 쿼리를 보내 비정상 로그(에러, 경고 등)만 조회
- 조회된 로그를 AI 서비스인 Upstage Solar LLM에 전달
- 로그를 사람이 이해하기 쉬운 요약 형태로 변환
이 단계에서 핵심은 모든 로그가 아니라 의미 있는 로그만 선별해 분석한다는 점입니다.
이를 통해 요약 결과의 밀도를 높이고, 불필요한 정보는 줄입니다.
4. 요약 결과 전달
최종적으로 생성된 요약 리포트는 SNS를 통해 전송됩니다.
Discord와 같은 메신저로 결과를 받아볼 수 있으며, 개발자는 별도의 대시보드에 접속하지 않아도 주요 로그 이슈를 바로 확인할 수 있습니다.
핵심 기술
핵심적으로 사용된 기술은 다음과 같습니다.
| 기술 | 역할 |
| Python 3.12 | Lambda 함수 및 분석 로직 실행 |
| AWS Lambda | 서버리스로 로그 분석 실행 자동화 |
| Amazon EventBridge | 정기 트리거 scheduling |
| Grafana Loki + Promtail | 로그 수집 및 저장 |
| Upstage Solar LLM | AI 기반 로그 요약 |
| Docker & GitHub Actions | CI/CD 자동 배포 |
주요 코드 설명
전체 코드는 아래 Github에서 확인할 수 있습니다.
https://github.com/snorose/daily-log-summarizer
GitHub - snorose/daily-log-summarizer: 로그 분석 람다
로그 분석 람다. Contribute to snorose/daily-log-summarizer development by creating an account on GitHub.
github.com
# Upstage API 키 로드
def get_api_key():
try:
response = ssm_client.get_parameter(Name=UPSTAGE_API_KEY_PARAMETER_NAME, WithDecryption=True)
return response['Parameter']['Value']
except Exception as e:
print(f"Error retrieving API Key: {e}")
raise e
# Upstage 클라이언트 초기화
UPSTAGE_API_KEY = get_api_key()
client = OpenAI(
api_key=UPSTAGE_API_KEY,
base_url="https://api.upstage.ai/v1"
)
이 코드는 Upstage API 키를 소스 코드에 직접 노출하지 않고, AWS Systems Manager Parameter Store에서 안전하게 불러와 Upstage API 클라이언트를 초기화하는 역할을 합니다.
먼저 get_api_key() 함수는 ssm_client.get_parameter()를 사용해 암호화된 API 키를 복호화하여 조회합니다.
이를 통해 API 키를 코드나 Git 저장소에 포함하지 않아도 되고, 운영 환경에서도 인증 정보를 안전하게 관리할 수 있습니다.
이렇게 불러온 API 키를 사용해 OpenAI 클라이언트를 초기화하되, base_url을 https://api.upstage.ai/v1로 지정해 Upstage Solar API 엔드포인트를 사용하도록 설정합니다.
이 방식의 장점은 기존 OpenAI SDK 인터페이스를 그대로 유지하면서도, 별도의 복잡한 설정 없이 Upstage LLM을 연동할 수 있다는 점입니다.
Upstage는 모델에 대한 문서가 매우 잘 되어 있고, Upstage Education 홈페이지를 통해 모델을 이용한 프로젝트도 할 수 있습니다.
https://console.upstage.ai/docs/getting-started?api=chat-reasoning
Home
본 강의는 자연어 처리(NLP) 분야에 관심이 있는 학습자를 대상으로 한 심화 과정입니다. 참가자들은 자연어 처리 문제를 이해하고, 분석하여 구현 가능한 적절한 형태로 정의하는 방법에 대해
edu.upstage.ai
SYSTEM_INSTRUCTION = f"""
당신은 SRE 팀을 위한 로그 분석 AI입니다.
목표: 주어진 로그만으로 사건(incident)을 감지하고, 유사 로그를 하나로 묶어(지문/패턴) 요약하며, 영향도·가설·다음 조치·조회용 질의를 제시합니다.
규칙:
- 외부 지식 추정 금지. 주어진 로그 범위 내에서만 판단.
- 불확실하면 "불확실"로 표기하고 confidence를 낮게 설정.
- 엔터티 정규화: service, env, region, host, error_code.
- 지문(fingerprint)은 변수값 마스킹(예: ID/IP 등) 후 간결하게.
- 샘플 로그(sample_logs)는 3개 이하, 비밀키/토큰/개인정보는 마스킹.
- 한국어로 작성.
[중요] 반드시 다음 JSON 형식을 준수하여 응답해야 합니다:
{json.dumps(JSON_SCHEMA, ensure_ascii=False)}
"""
AI가 로그를 요약하는 과정에서 엉뚱한 해석이나 불필요한 추정을 하지 않도록 시스템 프롬프트를 통해 명확한 규칙을 정의했습니다.
먼저 AI의 역할을 분명히 했습니다.
“당신은 SRE 팀을 위한 로그 분석 AI입니다.”라는 식으로 아이덴티티를 고정해, 일반적인 설명형 답변이 아니라 운영 관점에서 로그를 해석하는 역할에 집중하도록 유도했습니다.
그 다음으로는 동작 규칙을 세밀하게 설정했습니다.
로그에 포함되지 않은 내용을 추정하거나 외부 지식을 끌어오는 행위를 금지했고, 모든 응답은 한국어로 작성하도록 제한했습니다.
또한 실제 운영 로그를 다루는 만큼, IP 주소나 토큰과 같은 민감 정보는 반드시 마스킹하도록 가이드라인을 포함했습니다.
마지막으로, 응답 형식을 프로그램이 바로 후처리할 수 있도록 JSON 형식으로만 응답하도록 제한했습니다.
이를 통해 요약 결과를 알림 메시지로 보내거나, 추가 자동화를 연결하는 작업이 훨씬 수월해졌습니다.
Hands-on
작성된 코드를 기반으로 간단히 Hands-on을 할 수 있는 가이드를 작성해 보았습니다.
로컬에서 간단히 할 수 있도록 수정해 보았습니다.
test-logs/app.log
→ promtail
→ loki
→ summarize.py
→ Discord webhook
실행 흐름은 위와 같이 app.log를 요약해 Discord로 전달하게 됩니다.
version: "3.8"
services:
loki:
image: grafana/loki:2.9.0
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
grafana:
image: grafana/grafana:10.4.0
container_name: grafana
ports:
- "3000:3000"
promtail:
image: grafana/promtail:2.9.0
container_name: promtail
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml
- ./test-logs:/var/log/test-logs
command: -config.file=/etc/promtail/config.yml
로컬 환경에 Loki와 Grafana를 실행하기 위한 docker-compose.yml 파일을 생성해 위의 내용을 입력합니다.
server:
http_listen_port: 9080
grpc_listen_port: 0
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: test_logs
static_configs:
- targets: ["localhost"]
labels:
job: test_logs
__path__: /var/log/test-logs/*.log
또한 로그 수집을 위해 promtail-config.yml을 생성해 위의 내용을 입력합니다.
"[2025-02-01 14:05:01] ERROR o.a.c.c.C.[Tomcat].[localhost] - Servlet.service() for servlet [dispatcherServlet] threw exception",
"[2025-02-01 14:05:01] ERROR o.s.b.w.s.ErrorPageFilter - Forwarding to error page from request [/api/users/me] due to exception",
"[2025-02-01 14:05:01] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 0, SQLState: 08001",
"[2025-02-01 14:05:01] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - Communications link failure",
"[2025-02-01 14:05:01] ERROR c.z.h.HikariPool - HikariPool-1 - Connection is not available, request timed out after 30000ms",
"[2025-02-01 14:05:05] FATAL c.z.h.HikariPool - HikariPool-1 - Pool is exhausted, shutting down",
"[2025-02-01 14:05:06] ERROR o.s.b.SpringApplication - Application run failed",
"[2025-02-01 14:05:06] ERROR o.s.b.d.LoggingFailureAnalysisReporter - APPLICATION FAILED TO START",
"[2025-02-01 14:05:06] ERROR o.s.b.d.LoggingFailureAnalysisReporter - Description: Failed to initialize database connection",
"[2025-02-01 14:05:06] ERROR o.s.b.d.LoggingFailureAnalysisReporter - Action: Check database availability and credentials"
test-logs/app.log 경로에 다음과 같이 에러 로그를 넣습니다.
docker compose up -d
파일을 모두 생성했다면 docker compose로 컨테이너를 띄웁니다.
Grafana 로그인은 기본 admin / admin 입니다.
- Grafana UI: http://localhost:3000
- Loki API: http://localhost:3100
curl -G "http://localhost:3100/loki/api/v1/query" \
--data-urlencode 'query={job="test_logs"}'
컨테이너가 전부 실행 중이라면 위의 명령어로 로그가 출력되는지 확인합니다.
pip install requests python-dotenv openai
로컬에 필요한 라이브러리를 설치합니다.
# Loki
LOKI_URL=http://localhost:3100
# Upstage Solar
UPSTAGE_API_KEY=YOUR_UPSTAGE_KEY
# Discord
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/
루트 경로에 .env 파일을 생성해 Loki endpoint, API Key, Discord Webhook을 저장합니다.

API Key의 경우 https://console.upstage.ai/api-keys 에 들어가서 발급받을 수 있습니다.

Discord Webhook의 경우 채널 설정 > 연동 > 웹후크 만들기 > 웹후크 URL 복사로 확인할 수 있습니다.
import os
import json
import datetime
import requests
import re
from datetime import timezone, timedelta
from dotenv import load_dotenv
from openai import OpenAI
# 환경변수 로드
load_dotenv()
LOKI_URL = os.environ.get("LOKI_URL", "http://localhost:3100").rstrip("/")
UPSTAGE_API_KEY = os.environ.get("UPSTAGE_API_KEY")
DISCORD_WEBHOOK_URL = os.environ.get("DISCORD_WEBHOOK_URL")
if not LOKI_URL:
raise RuntimeError("LOKI_URL is required")
if not DISCORD_WEBHOOK_URL:
raise RuntimeError("DISCORD_WEBHOOK_URL is required")
if not UPSTAGE_API_KEY:
raise RuntimeError("UPSTAGE_API_KEY is required (Solar 요약용)")
LOKI_JOB_LABELS_TO_ANALYZE = [
"test_logs"
]
# Upstage Solar
client = OpenAI(
api_key=UPSTAGE_API_KEY,
base_url="https://api.upstage.ai/v1"
)
# JSON 출력 스키마
JSON_SCHEMA = {
"type":"object",
"properties":{
"time_window":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},
"incidents":{"type":"array"},
"summary_md":{"type":"string"},
"confidence_overall":{"type":"number"}
},
"required":["time_window","incidents","summary_md","confidence_overall"]
}
SYSTEM_INSTRUCTION = f"""
당신은 SRE 팀을 위한 로그 분석 AI입니다.
목표: 주어진 로그만으로 사건(incident)을 감지하고, 유사 로그를 하나로 묶어(지문/패턴) 요약하며, 영향도·가설·다음 조치·조회용 질의를 제시합니다.
규칙:
- 외부 지식 추정 금지. 주어진 로그 범위 내에서만 판단.
- 불확실하면 "불확실"로 표기하고 confidence를 낮게 설정.
- 엔터티 정규화: service, env, region, host, error_code.
- 지문(fingerprint)은 변수값 마스킹(예: ID/IP 등) 후 간결하게.
- 샘플 로그(sample_logs)는 3개 이하, 비밀키/토큰/개인정보는 마스킹.
- 한국어로 작성.
[중요] 반드시 다음 JSON 형식을 준수하여 응답해야 합니다:
{json.dumps(JSON_SCHEMA, ensure_ascii=False)}
""".strip()
def call_upstage_json(prompt: str) -> str:
"""
- JSON 모드 강제
- 실패하면 재시도 1회
"""
try:
response = client.chat.completions.create(
model="solar-mini",
messages=[
{"role": "system", "content": SYSTEM_INSTRUCTION},
{"role": "user", "content": prompt}
],
response_format={"type": "json_object"},
temperature=0.2,
stream=False
)
return response.choices[0].message.content
except Exception as e:
print(f"[WARN] Initial Upstage call failed: {e}. Retrying once...")
retry_prompt = prompt + "\n\n주의: 반드시 유효한 JSON 형식으로만 응답해야 합니다."
response = client.chat.completions.create(
model="solar-mini",
messages=[
{"role": "system", "content": SYSTEM_INSTRUCTION},
{"role": "user", "content": retry_prompt}
],
response_format={"type": "json_object"},
temperature=0.2,
stream=False
)
return response.choices[0].message.content
def build_user_prompt(from_iso, to_iso, logs_chunk):
return f"""
분석 대상 환경 정보:
- env: local
- service: daily-log-summarizer-hands-on
- region: local
- 기간: {from_iso} ~ {to_iso}
- 알려진 변경: 없음
다음 로그를 분석해주세요:
---LOGS START---
{logs_chunk}
---LOGS END---
""".strip()
# 민감 정보 마스킹
SECRET_PATTERNS = [
re.compile(r'(?i)(api[_-]?key|token|secret)\s*[:=]\s*([A-Za-z0-9\-\._]{8,})'),
re.compile(r'[0-9]{1,3}(?:\.[0-9]{1,3}){3}'),
re.compile(r'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}')
]
def mask_secrets(text: str) -> str:
for pat in SECRET_PATTERNS:
text = pat.sub('[REDACTED]', text)
return text
# Loki QueryRange
def query_loki_range(job_label: str, start_ns: int, end_ns: int,
include_keywords: str, exclude_keywords: str,
limit: int = 2000) -> list[str]:
"""
Loki에서 특정 job 라벨 로그를 조회해 문자열 리스트로 반환
"""
loki_query = f'{{job="{job_label}"}} |~ "{include_keywords}" !~ "{exclude_keywords}"'
url = f"{LOKI_URL}/loki/api/v1/query_range"
params = {
"query": loki_query,
"start": str(start_ns),
"end": str(end_ns),
"direction": "forward",
"limit": limit
}
print(f"[INFO] Querying Loki: {loki_query}")
r = requests.get(url, params=params, timeout=30)
r.raise_for_status()
loki_data = r.json()
logs = []
if loki_data.get("data", {}).get("resultType") != "streams":
return logs
for stream in loki_data["data"]["result"]:
for entry in stream.get("values", []):
ts_ns = int(entry[0])
line = entry[1]
ts_str = datetime.datetime.fromtimestamp(ts_ns / 1e9).strftime("%Y-%m-%d %H:%M:%S")
logs.append(f"[{ts_str}] {line}")
return logs
# Discord 전송
def send_to_discord(message: str):
if not DISCORD_WEBHOOK_URL:
print("[WARN] DISCORD_WEBHOOK_URL not set. Skipping.")
return
if len(message) > 1900:
message = message[:1900] + "\n...(truncated)"
payload = {"content": message}
r = requests.post(DISCORD_WEBHOOK_URL, json=payload, timeout=10)
r.raise_for_status()
def main():
# KST 기준 시간 계산
KST = timezone(timedelta(hours=9))
now_utc = datetime.datetime.now(timezone.utc)
now_kst = now_utc.astimezone(KST)
start_kst = now_kst - timedelta(minutes=30)
start_utc = start_kst.astimezone(timezone.utc)
start_time_ns = int(start_utc.timestamp() * 1e9)
end_time_ns = int(now_utc.timestamp() * 1e9)
include_keywords = "ERROR|failed|500|Exception|FATAL"
exclude_keywords = "IllegalArgumentException|AccessDeniedException"
all_error_logs = []
# Loki에서 가져오기
for job_label in LOKI_JOB_LABELS_TO_ANALYZE:
try:
logs = query_loki_range(
job_label=job_label,
start_ns=start_time_ns,
end_ns=end_time_ns,
include_keywords=include_keywords,
exclude_keywords=exclude_keywords
)
all_error_logs.extend(logs)
except Exception as e:
print(f"[WARN] Loki query failed for job={job_label}: {e}")
kst_start_time = start_utc.astimezone(KST)
kst_current_time = now_utc.astimezone(KST)
# 로그가 없으면 Discord에 안정 메시지
if not all_error_logs:
msg = (
f"✅ **{kst_start_time.strftime('%H:%M')}~{kst_current_time.strftime('%H:%M')} 시스템 안정**\n"
f"- ERROR/Exception 로그가 발견되지 않았습니다."
)
send_to_discord(msg)
print("[INFO] No critical logs found.")
return
unique_logs = sorted(list(set(all_error_logs)))
logs_to_summarize = mask_secrets("\n".join(unique_logs))
from_iso = start_utc.isoformat().replace("+00:00", "Z")
to_iso = now_utc.isoformat().replace("+00:00", "Z")
prompt = build_user_prompt(from_iso, to_iso, logs_to_summarize[:15000])
print(f"[INFO] Sending {len(unique_logs)} unique abnormal logs to Upstage API...")
summary_text = "요약 생성에 실패했습니다."
try:
upstage_json_str = call_upstage_json(prompt)
result = json.loads(upstage_json_str)
summary_text = result.get(
"summary_md",
"Upstage가 로그를 분석했지만 요약할 만한 중요 사건을 발견하지 못했습니다."
)
except Exception as e:
print(f"[WARN] JSON summary failed: {e}. Falling back to plain summary.")
fallback_prompt = (
"다음 로그들을 보고, 현재 발생한 문제 상황을 2~3문장의 자연스러운 한국어로 간단명료하게 요약해줘.\n\n"
+ logs_to_summarize[:12000]
)
try:
fallback_response = client.chat.completions.create(
model="solar-mini",
messages=[
{"role": "system", "content": "You are a helpful SRE assistant."},
{"role": "user", "content": fallback_prompt}
],
temperature=0.2
)
summary_text = fallback_response.choices[0].message.content
except Exception as fallback_e:
summary_text = f"분석 및 폴백 모두 실패: {fallback_e}"
# Discord로 전송
discord_message = (
f"🚨 **{kst_start_time.strftime('%H:%M')}~{kst_current_time.strftime('%H:%M')} 비정상 로그 발생**\n\n"
f"{summary_text}"
)
send_to_discord(discord_message)
print("[INFO] Summary sent to Discord.")
if __name__ == "__main__":
main()
로컬 경로에 summarize.py를 생성해 위의 내용을 붙여 넣어주세요.
Github의 코드를 로컬에서 돌릴 수 있도록 환경변수 관련 내용을 수정한 코드입니다.
python3 summarize.py
그 뒤 python 코드를 실행하면 디스코드 웹훅을 통해 디스코드에 에러 로그 분석이 전달되는 것을 확인할 수 있습니다.
결과 및 기대 효과
all_error_logs = [
"[2025-02-01 14:05:01] ERROR o.a.c.c.C.[Tomcat].[localhost] - Servlet.service() for servlet [dispatcherServlet] threw exception",
"[2025-02-01 14:05:01] ERROR o.s.b.w.s.ErrorPageFilter - Forwarding to error page from request [/api/users/me] due to exception",
"[2025-02-01 14:05:01] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 0, SQLState: 08001",
"[2025-02-01 14:05:01] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - Communications link failure",
"[2025-02-01 14:05:01] ERROR c.z.h.HikariPool - HikariPool-1 - Connection is not available, request timed out after 30000ms",
"[2025-02-01 14:05:05] FATAL c.z.h.HikariPool - HikariPool-1 - Pool is exhausted, shutting down",
"[2025-02-01 14:05:06] ERROR o.s.b.SpringApplication - Application run failed",
"[2025-02-01 14:05:06] ERROR o.s.b.d.LoggingFailureAnalysisReporter - APPLICATION FAILED TO START",
"[2025-02-01 14:05:06] ERROR o.s.b.d.LoggingFailureAnalysisReporter - Description: Failed to initialize database connection",
"[2025-02-01 14:05:06] ERROR o.s.b.d.LoggingFailureAnalysisReporter - Action: Check database availability and credentials"
]
테스트를 위해 다음과 같은 에러 코드를 준비했습니다.
Spring Boot, Tomcat, Hibernate, HikariCP, 그리고 DB를 사용하는 환경에서 흔히 볼 수 있는 로그입니다.
처음에는 단순한 SQL 오류처럼 보이지만, 로그를 시간 순서대로 따라가다 보면 DB 연결 장애 → 커넥션 풀 고갈 → 애플리케이션 종료로 이어지는 전형적인 장애 시나리오라는 것을 알 수 있습니다.
이런 에러가 실제로 발생했을 경우, 기존 방식이라면 다음과 같은 과정이 필요합니다.
- 누군가 로그를 발견한다
- 여러 줄로 흩어진 에러 로그를 시간 순서로 정리한다
- DB 문제인지, 애플리케이션 문제인지 추측한다
- 관련 명령어를 찾아 DB 상태, 커넥션 수, 애플리케이션 상태를 하나씩 확인한다
이 모든 과정은 결국 사람의 경험과 시간에 크게 의존합니다.
야간이나 주말이라면 대응은 더 늦어질 수밖에 없습니다.

Daily Log Summarizer를 도입하면 이 흐름이 달라집니다.
시스템은 발생한 에러 로그를 자동으로 수집하고, AI가 로그 간의 흐름을 분석해 어떤 문제가 발생했고, 어떻게 확산되었는지를 요약합니다.
개발자나 운영자는 더 이상 모든 로그를 직접 읽지 않아도, DB 연결 장애로 인해 커넥션 풀이 고갈되었고, 그 결과 애플리케이션이 종료되었다는 핵심만 빠르게 파악할 수 있습니다.
또한 대응을 위한 쿼리문을 제시해 어느 부분을 어떻게 살펴보면 될지 제시합니다.
이를 통해 원인 분석에 대한 시간 뿐만 아니라 누구든지 이 알림을 본 사람은 해당 문제에 대해 1차적으로 대응할 수 있습니다.
결과적으로 Daily Log Summarizer는 로그를 단순한 기록이 아니라, 운영 판단을 돕는 요약된 정보로 바꿔주는 역할을 합니다.