inpaintServer/app/utils/daily_stats.py

204 lines
7.1 KiB
Python

"""
일일 통계 수집 및 관리
- 처리된 이미지 수
- 네트워크 전송량
- API 호출 통계
"""
from __future__ import annotations
import os
import time
import json
from datetime import datetime, timedelta
from typing import Dict, Any
from threading import Lock
from collections import defaultdict
LOG_DIR = "logs"
os.makedirs(LOG_DIR, exist_ok=True)
DAILY_STATS_PATH = os.path.join(LOG_DIR, "daily_stats.json")
class DailyStatsCollector:
def __init__(self):
self.lock = Lock()
self.current_date = datetime.now().strftime("%Y-%m-%d")
self.stats = self._load_or_create_today_stats()
def _load_or_create_today_stats(self) -> Dict[str, Any]:
"""오늘의 통계를 로드하거나 새로 생성"""
today = datetime.now().strftime("%Y-%m-%d")
try:
if os.path.exists(DAILY_STATS_PATH):
with open(DAILY_STATS_PATH, "r", encoding="utf-8") as f:
all_stats = json.load(f)
# 오늘 날짜의 통계가 있으면 반환
if today in all_stats:
return all_stats[today]
except Exception:
pass
# 새로운 통계 생성
return {
"date": today,
"images_processed": {
"inpaint": 0,
"remove_bg": 0,
"gen_image": 0,
"total": 0
},
"network": {
"bytes_uploaded": 0,
"bytes_downloaded": 0,
"requests_count": 0
},
"api_calls": {
"total": 0,
"success": 0,
"failed": 0
},
"models_used": defaultdict(int),
"peak_concurrent": 0,
"start_time": time.time(),
"last_update": time.time()
}
def _check_date_rollover(self):
"""날짜가 바뀌면 통계 저장 및 리셋"""
today = datetime.now().strftime("%Y-%m-%d")
if today != self.current_date:
# 어제 통계 저장
self._save_stats()
# 새로운 날짜로 리셋
self.current_date = today
self.stats = self._load_or_create_today_stats()
def record_image_processed(self, endpoint_type: str):
"""이미지 처리 기록"""
with self.lock:
self._check_date_rollover()
if endpoint_type == "inpaint":
self.stats["images_processed"]["inpaint"] += 1
elif endpoint_type == "remove_bg":
self.stats["images_processed"]["remove_bg"] += 1
elif endpoint_type == "gen_image":
self.stats["images_processed"]["gen_image"] += 1
self.stats["images_processed"]["total"] += 1
self.stats["last_update"] = time.time()
def record_network_traffic(self, bytes_uploaded: int, bytes_downloaded: int):
"""네트워크 트래픽 기록"""
with self.lock:
self._check_date_rollover()
self.stats["network"]["bytes_uploaded"] += bytes_uploaded
self.stats["network"]["bytes_downloaded"] += bytes_downloaded
self.stats["network"]["requests_count"] += 1
self.stats["last_update"] = time.time()
def record_api_call(self, success: bool):
"""API 호출 기록"""
with self.lock:
self._check_date_rollover()
self.stats["api_calls"]["total"] += 1
if success:
self.stats["api_calls"]["success"] += 1
else:
self.stats["api_calls"]["failed"] += 1
self.stats["last_update"] = time.time()
def record_model_usage(self, model_name: str):
"""모델 사용 기록"""
with self.lock:
self._check_date_rollover()
if "models_used" not in self.stats:
self.stats["models_used"] = {}
self.stats["models_used"][model_name] = self.stats["models_used"].get(model_name, 0) + 1
self.stats["last_update"] = time.time()
def update_peak_concurrent(self, current_concurrent: int):
"""최대 동시 요청 수 업데이트"""
with self.lock:
self._check_date_rollover()
if current_concurrent > self.stats["peak_concurrent"]:
self.stats["peak_concurrent"] = current_concurrent
self.stats["last_update"] = time.time()
def get_today_stats(self) -> Dict[str, Any]:
"""오늘의 통계 반환"""
with self.lock:
self._check_date_rollover()
# 읽기 전용 복사본 반환
stats_copy = dict(self.stats)
# MB/GB 단위로 변환된 값 추가
stats_copy["network"]["mb_uploaded"] = stats_copy["network"]["bytes_uploaded"] / (1024 * 1024)
stats_copy["network"]["mb_downloaded"] = stats_copy["network"]["bytes_downloaded"] / (1024 * 1024)
stats_copy["network"]["gb_uploaded"] = stats_copy["network"]["bytes_uploaded"] / (1024 * 1024 * 1024)
stats_copy["network"]["gb_downloaded"] = stats_copy["network"]["bytes_downloaded"] / (1024 * 1024 * 1024)
return stats_copy
def get_historical_stats(self, days: int = 7) -> Dict[str, Any]:
"""최근 N일간의 통계 반환"""
try:
if not os.path.exists(DAILY_STATS_PATH):
return {}
with open(DAILY_STATS_PATH, "r", encoding="utf-8") as f:
all_stats = json.load(f)
# 최근 N일 필터링
cutoff_date = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
recent_stats = {
date: stats for date, stats in all_stats.items()
if date >= cutoff_date
}
return recent_stats
except Exception:
return {}
def _save_stats(self):
"""현재 통계를 파일에 저장"""
try:
# 기존 통계 로드
all_stats = {}
if os.path.exists(DAILY_STATS_PATH):
with open(DAILY_STATS_PATH, "r", encoding="utf-8") as f:
all_stats = json.load(f)
# 현재 날짜 통계 업데이트
all_stats[self.current_date] = self.stats
# 30일 이상 된 통계 삭제
cutoff_date = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d")
all_stats = {
date: stats for date, stats in all_stats.items()
if date >= cutoff_date
}
# 파일에 저장
with open(DAILY_STATS_PATH, "w", encoding="utf-8") as f:
json.dump(all_stats, f, indent=2, ensure_ascii=False)
except Exception:
pass
def save(self):
"""수동 저장"""
with self.lock:
self._save_stats()
# 글로벌 인스턴스
daily_stats = DailyStatsCollector()