security
CHAPTER 77 / 84
읽기 약 2분
FUNCTION
로그 시각화와 리포트
핵심 개념
collections.Counter + matplotlib/plotly로 24시간 로그 분석 대시보드를 만든다.
본문
핵심 통계 — Counter 기반
# ⚠️ 이 코드는 허가된 환경에서만 사용하세요.
from collections import Counter, defaultdict
from datetime import datetime
import re
APACHE_COMBINED = re.compile(
r'^(?P<ip>\S+)\s+\S+\s+\S+\s+'
r'\[(?P<time>[^\]]+)\]\s+'
r'"(?P<method>\S+)\s+(?P<path>\S+)\s+\S+"\s+'
r'(?P<status>\d{3})\s+(?P<bytes>\S+)\s+'
r'"(?P<referer>[^"]*)"\s+"(?P<user_agent>[^"]*)"'
)
def analyze_log(log_path: str) -> dict:
"""단일 순회로 모든 통계 수집."""
stats = {
'total': 0,
'ips': Counter(),
'paths': Counter(),
'methods': Counter(),
'statuses': Counter(),
'user_agents': Counter(),
'hourly': Counter(), # 시간대별 (00~23)
'bytes_total': 0,
}
with open(log_path, encoding='utf-8', errors='replace') as f:
for line in f:
m = APACHE_COMBINED.match(line)
if not m:
continue
stats['total'] += 1
stats['ips'][m['ip']] += 1
stats['paths'][m['path']] += 1
stats['methods'][m['method']] += 1
stats['statuses'][int(m['status'])] += 1
stats['user_agents'][m['user_agent'][:80]] += 1
# 시간대 추출
try:
t = datetime.strptime(m['time'].split()[0], '%d/%b/%Y:%H:%M:%S')
stats['hourly'][t.hour] += 1
except ValueError:
pass
# 바이트
try:
stats['bytes_total'] += int(m['bytes'])
except ValueError:
pass
return statsmatplotlib 시각화
# pip install matplotlib
import matplotlib.pyplot as plt
def plot_dashboard(stats: dict, output_path: str = 'log_dashboard.png'):
"""4개 차트 종합 대시보드."""
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('웹 로그 분석 대시보드', fontsize=16, fontweight='bold')
# 1. 시간대별 요청 수
hours = list(range(24))
counts = [stats['hourly'].get(h, 0) for h in hours]
axes[0, 0].bar(hours, counts, color='#6366f1')
axes[0, 0].set_title('시간대별 요청 수')
axes[0, 0].set_xlabel('시간 (24h)')
axes[0, 0].set_ylabel('요청 수')
axes[0, 0].set_xticks(hours[::2])
# 2. 상태 코드 분포
statuses = sorted(stats['statuses'].items())
codes = [str(s[0]) for s in statuses]
counts = [s[1] for s in statuses]
colors = ['#10b981' if int(c) < 400 else '#f59e0b' if int(c) < 500 else '#ef4444' for c in codes]
axes[0, 1].bar(codes, counts, color=colors)
axes[0, 1].set_title('상태 코드 분포')
axes[0, 1].set_xlabel('HTTP 상태')
# 3. Top 10 IP
top_ips = stats['ips'].most_common(10)
ips = [x[0] for x in reversed(top_ips)]
counts = [x[1] for x in reversed(top_ips)]
axes[1, 0].barh(ips, counts, color='#a78bfa')
axes[1, 0].set_title('Top 10 IP (요청 수)')
# 4. Top 10 Path
top_paths = stats['paths'].most_common(10)
paths = [x[0][:30] for x in reversed(top_paths)]
counts = [x[1] for x in reversed(top_paths)]
axes[1, 1].barh(paths, counts, color='#34d399')
axes[1, 1].set_title('Top 10 경로 (요청 수)')
plt.tight_layout()
plt.savefig(output_path, dpi=150, bbox_inches='tight')
plt.close()
return output_pathPlotly — 인터랙티브 HTML 대시보드
# pip install plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
def plotly_dashboard(stats: dict, output_path: str = 'dashboard.html'):
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('시간대별 요청', '상태 코드', 'Top IP', 'Top Path'),
)
# 시간대별
hours = list(range(24))
fig.add_trace(go.Bar(
x=hours,
y=[stats['hourly'].get(h, 0) for h in hours],
marker_color='#6366f1',
), row=1, col=1)
# 상태 코드
statuses = sorted(stats['statuses'].items())
fig.add_trace(go.Bar(
x=[str(s[0]) for s in statuses],
y=[s[1] for s in statuses],
marker_color=['#10b981' if s[0] < 400 else '#f59e0b' if s[0] < 500 else '#ef4444' for s in statuses],
), row=1, col=2)
# Top IP
top_ips = stats['ips'].most_common(10)
fig.add_trace(go.Bar(
x=[x[1] for x in top_ips],
y=[x[0] for x in top_ips],
orientation='h',
marker_color='#a78bfa',
), row=2, col=1)
# Top Path
top_paths = stats['paths'].most_common(10)
fig.add_trace(go.Bar(
x=[x[1] for x in top_paths],
y=[x[0][:30] for x in top_paths],
orientation='h',
marker_color='#34d399',
), row=2, col=2)
fig.update_layout(
height=800,
title_text='웹 로그 분석 대시보드',
showlegend=False,
)
fig.write_html(output_path)
return output_pathCSV 리포트
import csv
def export_csv(stats: dict, output_path: str = 'log_report.csv'):
"""주요 통계를 CSV로 저장 — Excel 분석용."""
with open(output_path, 'w', newline='', encoding='utf-8-sig') as f:
w = csv.writer(f)
w.writerow(['지표', '값'])
w.writerow(['총 요청 수', stats['total']])
w.writerow(['총 트래픽 (MB)', f'{stats["bytes_total"] / 1024 / 1024:.2f}'])
w.writerow([])
w.writerow(['Top 10 IP'])
for ip, count in stats['ips'].most_common(10):
w.writerow([ip, count])
w.writerow([])
w.writerow(['상태 코드 분포'])
for status, count in sorted(stats['statuses'].items()):
w.writerow([status, count])JSON 리포트
import json
def export_json(stats: dict, output_path: str = 'log_report.json'):
"""기계 읽기 좋은 형식 — 다른 도구 연동용."""
serializable = {
'total': stats['total'],
'bytes_total': stats['bytes_total'],
'top_ips': stats['ips'].most_common(20),
'top_paths': stats['paths'].most_common(20),
'methods': dict(stats['methods']),
'statuses': dict(stats['statuses']),
'hourly': dict(stats['hourly']),
'top_user_agents': stats['user_agents'].most_common(20),
'generated_at': datetime.now().isoformat(),
}
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(serializable, f, indent=2, ensure_ascii=False)실습: 24시간 분석 자동화
def daily_report(log_path: str, output_dir: str = 'reports'):
"""매일 실행되는 분석 → PNG + HTML + CSV + JSON 4종 출력."""
import os
os.makedirs(output_dir, exist_ok=True)
today = datetime.now().strftime('%Y-%m-%d')
print(f'📊 분석 시작: {log_path}')
stats = analyze_log(log_path)
plot_dashboard(stats, f'{output_dir}/dashboard_{today}.png')
plotly_dashboard(stats, f'{output_dir}/dashboard_{today}.html')
export_csv(stats, f'{output_dir}/report_{today}.csv')
export_json(stats, f'{output_dir}/report_{today}.json')
print(f'\n총 요청: {stats["total"]:,}')
print(f'트래픽: {stats["bytes_total"] / 1024 / 1024:.1f} MB')
print(f'Top IP: {stats["ips"].most_common(3)}')
print(f'\n📁 저장: {output_dir}/')⚠️ 시각화 흔한 실수
- 너무 많은 카테고리 → 비교 어려움 (Top 10이 적정)
- y축 0부터 안 시작 → 차이 과장
- 색상이 의미 전달 안 함 → 빨강/노랑/초록으로 상태 매핑
- PII(개인정보) 노출 — IP를 마스킹하거나 해시 처리
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 로그 분석 코드의 Counter·시각화·메모리 사용을 분석하고 10GB+ 로그도 처리할 수 있게 개선해줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
matplotlib vs plotly vs Streamlit 로그 대시보드 만들 때 각각의 장단점을 실전 코드로 비교해줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 한 달치 웹 로그를 분석해서 시간대·요일·경로별 트래픽 패턴과 이상 징후를 종합 리포트로 만들어줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 로그 시각화 트렌드 — 자체 plotly vs Grafana vs Datadog Dashboard 1인 운영자에게 가성비 좋은 선택을 솔직히 알려줘.
⭐ 이것만 기억하세요
로그 시각화와 리포트는 이 3가지만 확실히 잡으세요
1.Counter + matplotlib + plotly 조합으로 4개 차트 종합 대시보드를 한 함수로 만든다
2.4종 출력(PNG/HTML/CSV/JSON)을 자동화하면 매일 다른 사람과 데이터를 공유할 수 있다
3.다음 챕터에서 모듈 4 종합 — 로그 분석 자동화 도구 CLI를 완성한다
공유하기
진행도 77 / 84