Files
2026-04-07 14:11:45 +08:00

146 lines
4.6 KiB
Python

# ///
# log_service.py
# 描述:日志服务,支持Redis缓存和文件持久化
# 作者:AI Generated
# 创建日期:2026-04-05
# ///
import asyncio
import json
import os
from datetime import datetime
from typing import List, Dict, Any, Optional
from collections import deque
from config import get_settings
class LogService:
def __init__(self, max_logs: int = 500, log_file: str = "logs/app.log"):
self.max_logs = max_logs
self.log_file = log_file
self._logs: deque = deque(maxlen=max_logs)
self._subscribers: List[asyncio.Queue] = []
self._log_levels = ["INFO", "WARNING", "ERROR", "SUCCESS"]
self._redis_client = None
self._redis_available = False
self._init_redis()
os.makedirs(os.path.dirname(log_file), exist_ok=True)
self._load_from_file()
def _init_redis(self):
try:
import redis
settings = get_settings()
redis_config = settings.redis
self._redis_client = redis.from_url(
redis_config.url,
db=redis_config.db,
password=redis_config.password,
decode_responses=True
)
self._redis_client.ping()
self._redis_available = True
except Exception:
self._redis_available = False
self._redis_client = None
def _load_from_file(self):
if not os.path.exists(self.log_file):
return
try:
with open(self.log_file, "r", encoding="utf-8") as f:
for line in f:
if line.strip():
try:
log_entry = json.loads(line.strip())
if len(self._logs) < self.max_logs:
self._logs.append(log_entry)
except Exception:
continue
except Exception:
pass
def _save_to_file(self, log_entry: Dict[str, Any]):
try:
with open(self.log_file, "a", encoding="utf-8") as f:
f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
except Exception:
pass
def _save_to_redis(self, log_entry: Dict[str, Any]):
if not self._redis_available:
return
try:
self._redis_client.lpush("wxauto:logs", json.dumps(log_entry, ensure_ascii=False))
self._redis_client.ltrim("wxauto:logs", 0, self.max_logs - 1)
except Exception:
pass
def add_log(self, message: str, level: str = "INFO", source: str = "System"):
log_entry = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"level": level,
"source": source,
"message": message
}
self._logs.append(log_entry)
if self._redis_available:
self._save_to_redis(log_entry)
else:
self._save_to_file(log_entry)
self._notify_subscribers(log_entry)
def info(self, message: str, source: str = "System"):
self.add_log(message, "INFO", source)
def warning(self, message: str, source: str = "System"):
self.add_log(message, "WARNING", source)
def error(self, message: str, source: str = "System"):
self.add_log(message, "ERROR", source)
def success(self, message: str, source: str = "System"):
self.add_log(message, "SUCCESS", source)
async def subscribe(self) -> asyncio.Queue:
queue = asyncio.Queue(maxsize=100)
self._subscribers.append(queue)
return queue
def unsubscribe(self, queue: asyncio.Queue):
if queue in self._subscribers:
self._subscribers.remove(queue)
async def _notify_subscribers(self, log_entry: Dict[str, Any]):
for queue in self._subscribers:
try:
await queue.put(log_entry)
except Exception:
pass
def get_recent_logs(self, count: int = 50) -> List[Dict[str, Any]]:
if self._redis_available:
try:
logs = self._redis_client.lrange("wxauto:logs", 0, count - 1)
return [json.loads(log) for log in reversed(logs)]
except Exception:
pass
return list(self._logs)[-count:]
def get_all_logs(self) -> List[Dict[str, Any]]:
if self._redis_available:
try:
logs = self._redis_client.lrange("wxauto:logs", 0, self.max_logs - 1)
return [json.loads(log) for log in reversed(logs)]
except Exception:
pass
return list(self._logs)
log_service = LogService()