146 lines
4.6 KiB
Python
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()
|