📑 이 챕터에서 다룰 내용
운영이 시작되면 에러가 언제 터지는지, API 비용이 얼마나 나가는지 직접 확인하기 어렵습니다. Sentry로 에러를 자동 추적하고, 매일 리포트 cron으로 운영 현황을 자동 요약해두면 운영자가 자리를 비워도 시스템이 스스로 돌아갑니다.
- 예상 시간: 1시간
- Sentry SDK — 에러 자동 추적 (무료 5K events/월)
- Anthropic API 사용량 추적 — LogOnTable 확장
- 매일 운영 리포트 — cron 09:30 KST 자동 발송
- 사용자별 cooldown — rate limit 보호
1탄 v2 부록 H-2 R4 "운영자 휴식 + 시스템 자체 안정" → 모니터링 자동화입니다.
Sentry 가입 + DSN
sentry.io에 가입하면 무료로 월 5,000건 이벤트를 추적할 수 있어요. 봇 에러를 실시간으로 받아볼 수 있습니다.
- sentry.io 가입 (무료 5K events/월)
- New Project → Node.js (또는 Python) 선택
- DSN 복사 (
https://...@sentry.io/...)
Node.js 봇 통합
cd ~/tsv-bot npm install @sentry/node
// src/sentry.ts
import * as Sentry from '@sentry/node';
export function initSentry(): void {
if (!process.env.SENTRY_DSN) {
console.log('Sentry DSN not configured, skipping');
return;
}
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || 'development',
tracesSampleRate: 0.1,
beforeSend(event) {
// 사용자 PII 제거
if (event.user?.email) delete event.user.email;
return event;
},
});
console.log('✅ Sentry initialized');
}
export { Sentry };
// src/bot.ts (수정 - 첫 줄)
import 'dotenv/config';
import { initSentry, Sentry } from './sentry.js';
initSentry();
// 에러 발생 시
catch (err: any) {
Sentry.captureException(err, {
user: { id: msg.author.id },
extra: { question, channel: msg.channel.id },
});
// ... 기존 에러 처리
}
Python 봇 통합
cd ~/tsv-tg-bot source .venv/bin/activate uv pip install sentry-sdk
# src/sentry_init.py
import os
import sentry_sdk
def init_sentry():
dsn = os.environ.get("SENTRY_DSN")
if not dsn:
print("Sentry DSN not configured, skipping")
return
sentry_sdk.init(
dsn=dsn,
environment=os.environ.get("ENV", "development"),
traces_sample_rate=0.1,
send_default_pii=False,
)
print("✅ Sentry initialized")
# src/bot.py (첫 줄)
from sentry_init import init_sentry
init_sentry()
# 에러 발생 시
import sentry_sdk
try:
response = await ask_claude(question)
except Exception as e:
sentry_sdk.capture_exception(e)
# ... 기존 처리
DSN을 코드에 직접 넣으면 GitHub에 노출될 위험이 있어요. .env 파일에 SENTRY_DSN=https://...@sentry.io/...로 저장하고 .gitignore에 포함시키세요.
Anthropic Console 자체 추적
console.anthropic.com/settings/usage에서 일별·월별 토큰 사용량과 비용 추세를 확인할 수 있어요. 봇별로 API 키를 분리해두면 비용 추적이 더 편리합니다.
- 일별·월별 token 사용량
- 비용 추세
- API 키별 분리
★ Workspace 분리 권장
- 봇별 API 키 발급
- 각 봇 비용 분리 추적
- 한도 초과 시 봇별 차단
자체 추적 (LogOnTable 활용)
응답 객체의 usage 필드를 로그로 남기면 API 비용을 직접 계산할 수 있어요. E4 LogOnTable의 확장 적용입니다.
// src/usage_tracker.ts
import fs from 'fs';
import path from 'path';
interface UsageEntry {
timestamp: string;
model: string;
input_tokens: number;
output_tokens: number;
cost_usd: number;
}
const USAGE_FILE = path.join(
process.env.LOG_DIR || './logs',
'usage.jsonl'
);
export function logUsage(entry: UsageEntry): void {
fs.appendFileSync(USAGE_FILE, JSON.stringify(entry) + '\n');
}
// claude.ts 수정 — response.usage 사용
import { logUsage } from './usage_tracker.js';
export async function askClaude(question: string): Promise<string> {
// ... 기존 코드
const response = await client.messages.create(...);
// ★ 사용량 추적
if (response.usage) {
const inputCost = (response.usage.input_tokens / 1_000_000) * 3;
const outputCost = (response.usage.output_tokens / 1_000_000) * 15;
logUsage({
timestamp: new Date().toISOString(),
model: MODEL,
input_tokens: response.usage.input_tokens,
output_tokens: response.usage.output_tokens,
cost_usd: inputCost + outputCost,
});
}
// ... 기존 응답 추출
}
일별 비용 합계 cron
# /home/ubuntu/scripts/daily_cost.sh
cat > ~/scripts/daily_cost.sh << 'EOF'
#!/bin/bash
DATE=$(date -d "yesterday" +%Y-%m-%d)
LOG_FILE="/home/ubuntu/tsv-bot/logs/usage.jsonl"
if [ -f "$LOG_FILE" ]; then
TOTAL=$(jq -r --arg date "$DATE" \
'select(.timestamp | startswith($date)) | .cost_usd' \
"$LOG_FILE" 2>/dev/null | \
awk '{sum += $1} END {printf "%.4f", sum}')
echo "[$DATE] Anthropic API 비용: \$$TOTAL"
# 한도 초과 시 알림 (예: $5/일)
if (( $(echo "$TOTAL > 5" | bc -l) )); then
# Discord webhook 알림 (예시)
curl -H "Content-Type: application/json" \
-d "{\"content\":\"⚠️ Anthropic API 일별 비용 초과: \$$TOTAL\"}" \
"$DISCORD_ALERT_WEBHOOK"
fi
fi
EOF
chmod +x ~/scripts/daily_cost.sh
# crontab 등록 — 매일 09:00 KST
crontab -e
# 0 9 * * * /home/ubuntu/scripts/daily_cost.sh >> /home/ubuntu/logs/daily_cost.log 2>&1
매일 09:30 KST에 Discord·Telegram 봇 통계, API 비용, 시스템 상태, Position C 차단 이력을 자동으로 집계해서 Discord 웹훅으로 발송합니다.
# /home/ubuntu/scripts/daily_report.sh
cat > ~/scripts/daily_report.sh << 'EOF'
#!/bin/bash
DATE=$(date -d "yesterday" +%Y-%m-%d)
DISCORD_LOG="/home/ubuntu/tsv-bot/logs/${DATE}.jsonl"
TG_LOG="/home/ubuntu/tsv-tg-bot/logs/${DATE}.jsonl"
REPORT_FILE="/home/ubuntu/reports/${DATE}-report.txt"
mkdir -p /home/ubuntu/reports
{
echo "=== TSV Bot 일별 리포트 [$DATE] ==="
echo ""
# 디스코드 봇 통계
if [ -f "$DISCORD_LOG" ]; then
echo "--- 디스코드 봇 ---"
TOTAL=$(wc -l < "$DISCORD_LOG")
SUCCESS=$(jq -r 'select(.status == "success") | 1' "$DISCORD_LOG" | wc -l)
BLOCKED=$(jq -r 'select(.status | startswith("blocked")) | 1' "$DISCORD_LOG" | wc -l)
AVG_DUR=$(jq -r 'select(.status == "success") | .duration_ms' "$DISCORD_LOG" | \
awk '{sum += $1; cnt++} END {if (cnt > 0) printf "%.0f", sum/cnt; else print "0"}')
echo " 총 질문: $TOTAL"
echo " 성공: $SUCCESS"
echo " 차단: $BLOCKED (Position C)"
echo " 평균 응답 시간: ${AVG_DUR}ms"
echo ""
fi
# Telegram 봇 통계
if [ -f "$TG_LOG" ]; then
echo "--- Telegram 봇 ---"
TOTAL=$(wc -l < "$TG_LOG")
SUCCESS=$(jq -r 'select(.status == "success") | 1' "$TG_LOG" | wc -l)
AVG_DUR=$(jq -r 'select(.status == "success") | .duration_ms' "$TG_LOG" | \
awk '{sum += $1; cnt++} END {if (cnt > 0) printf "%.0f", sum/cnt; else print "0"}')
echo " 총 질문: $TOTAL"
echo " 성공: $SUCCESS"
echo " 평균 응답 시간: ${AVG_DUR}ms"
echo ""
fi
# API 비용
if [ -f "/home/ubuntu/tsv-bot/logs/usage.jsonl" ]; then
COST=$(jq -r --arg date "$DATE" \
'select(.timestamp | startswith($date)) | .cost_usd' \
"/home/ubuntu/tsv-bot/logs/usage.jsonl" 2>/dev/null | \
awk '{sum += $1} END {printf "%.4f", sum}')
echo "--- Anthropic API ---"
echo " 일별 비용: \$$COST"
echo " 월 추정: \$$(echo "$COST * 30" | bc -l | xargs printf "%.2f")"
echo ""
fi
# 시스템 상태
echo "--- 시스템 ---"
echo " 업타임: $(uptime -p)"
echo " 메모리: $(free -h | grep Mem | awk '{print $3 " / " $2}')"
echo " 디스크: $(df -h / | tail -1 | awk '{print $3 " / " $2}')"
echo ""
# 차단 이력 (Position C)
echo "--- 차단 이력 (Position C) ---"
if [ -f "$DISCORD_LOG" ]; then
jq -r 'select(.status | startswith("blocked")) | " - " + .timestamp + ": " + .question[:80]' \
"$DISCORD_LOG" | head -10
fi
} > "$REPORT_FILE"
# 디스코드 alert webhook 전송
if [ -n "$DISCORD_ALERT_WEBHOOK" ]; then
curl -H "Content-Type: application/json" \
--data "$(jq -Rs '{content: .}' < "$REPORT_FILE")" \
"$DISCORD_ALERT_WEBHOOK"
fi
cat "$REPORT_FILE"
EOF
chmod +x ~/scripts/daily_report.sh
# crontab — 매일 09:30 KST
crontab -e
# 30 9 * * * /home/ubuntu/scripts/daily_report.sh
- 에러 급증 시 다음 날 아침에 바로 파악할 수 있어요
- API 비용이 예상을 벗어나면 즉시 확인·조정 가능해요
- Position C 차단 이력으로 악용 시도를 추적할 수 있어요
- 운영자가 자리를 비워도 시스템 상태를 파악할 수 있습니다
한 사용자가 연속으로 질문을 보내면 Anthropic API 비용이 급증할 수 있어요. 사용자별 5초 쿨다운을 적용해서 과도한 요청을 막습니다.
// src/cooldown.ts
const userCooldowns = new Map<string, number>();
const COOLDOWN_MS = 5000; // 5초
export function checkCooldown(userId: string): {
ok: boolean;
remainingMs?: number;
} {
const now = Date.now();
const last = userCooldowns.get(userId) || 0;
const elapsed = now - last;
if (elapsed < COOLDOWN_MS) {
return { ok: false, remainingMs: COOLDOWN_MS - elapsed };
}
userCooldowns.set(userId, now);
return { ok: true };
}
// 정기 cleanup (메모리 누수 방지)
setInterval(() => {
const now = Date.now();
for (const [userId, time] of userCooldowns.entries()) {
if (now - time > 60_000) { // 1분 후 삭제
userCooldowns.delete(userId);
}
}
}, 60_000);
// bot.ts 수정
import { checkCooldown } from './cooldown.js';
const cooldownCheck = checkCooldown(msg.author.id);
if (!cooldownCheck.ok) {
await msg.reply(
`⏰ ${Math.ceil(cooldownCheck.remainingMs! / 1000)}초 후 다시 시도해주세요.`
);
return;
}
userCooldowns는 메모리에 저장되므로 pm2 reload나 서버 재시작 시 초기화됩니다.
영구 쿨다운이 필요하다면 Redis나 로컬 파일로 교체하세요. 대부분의 봇에서는 메모리 Map으로 충분합니다.
- 1️⃣ Sentry SDK: Node.js·Python 봇 모두 연동 — 에러 실시간 자동 추적 (무료 5K events/월)
- 2️⃣ API 사용량 추적:
response.usage→usage.jsonl기록 — E4 LogOnTable 확장 - 3️⃣ 일별 비용 집계:
daily_cost.shcron 09:00 — 한도 초과 시 Discord 알림 - 4️⃣ 매일 운영 리포트:
daily_report.shcron 09:30 — 봇 통계 + 비용 + 시스템 상태 + 차단 이력 - 5️⃣ 사용자 cooldown: 5초 제한 — API 비용 급증 방지 + 메모리 cleanup 포함
- 6️⃣ 메타 일관: 1탄 v2 "Day 0 시스템" → 운영자 휴식 동안 시스템 자체 안정 + 모니터링
Sentry + LogOnTable 확장 + 매일 리포트 cron = 운영자 휴식 동안 시스템 자체 안정.
다음 편에서는 git push 한 번으로 서버가 자동 배포되는 GitHub Actions를 설정합니다.