textai-v2 / modules /logger.py
rbt2025's picture
Deploy TextAI v2 - Clean architecture
02daacc verified
"""
Logging Service Module
Centralized logging with file persistence.
"""
import json
import threading
import uuid
from pathlib import Path
from datetime import datetime
from typing import List, Dict, Optional
from dataclasses import dataclass, asdict
from enum import Enum
from .config import LOGS_DIR
class LogLevel(Enum):
DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
ERROR = "ERROR"
EVENT = "EVENT"
@dataclass
class LogEntry:
timestamp: str
level: str
category: str
message: str
details: Optional[Dict] = None
log_id: str = ""
def __post_init__(self):
if not self.log_id:
self.log_id = str(uuid.uuid4())[:8]
class LoggingService:
"""Centralized logging service with file persistence"""
def __init__(self, log_dir: Path):
self.log_dir = log_dir
self.log_file = log_dir / "app.log"
self.event_log = log_dir / "events.log"
self.error_log = log_dir / "errors.log"
self.max_entries = 1000
self._lock = threading.Lock()
def _write_log(self, entry: LogEntry, file_path: Path):
with self._lock:
try:
logs = []
if file_path.exists():
try:
logs = json.loads(file_path.read_text())
except:
logs = []
logs.append(asdict(entry))
if len(logs) > self.max_entries:
logs = logs[-self.max_entries:]
file_path.write_text(json.dumps(logs, indent=2))
except Exception as e:
print(f"Log write error: {e}")
def log(self, level: LogLevel, category: str, message: str, details: Dict = None):
entry = LogEntry(
timestamp=datetime.now().isoformat(),
level=level.value,
category=category,
message=message,
details=details
)
self._write_log(entry, self.log_file)
if level == LogLevel.ERROR:
self._write_log(entry, self.error_log)
elif level == LogLevel.EVENT:
self._write_log(entry, self.event_log)
return entry.log_id
def info(self, category: str, message: str, details: Dict = None):
return self.log(LogLevel.INFO, category, message, details)
def error(self, category: str, message: str, details: Dict = None):
return self.log(LogLevel.ERROR, category, message, details)
def event(self, category: str, message: str, details: Dict = None):
return self.log(LogLevel.EVENT, category, message, details)
def warning(self, category: str, message: str, details: Dict = None):
return self.log(LogLevel.WARNING, category, message, details)
def get_logs(self, log_type: str = "all", limit: int = 100, level: str = None, category: str = None) -> List[Dict]:
file_map = {
"all": self.log_file,
"events": self.event_log,
"errors": self.error_log
}
file_path = file_map.get(log_type, self.log_file)
if not file_path.exists():
return []
try:
logs = json.loads(file_path.read_text())
if level:
logs = [l for l in logs if l.get("level") == level]
if category:
logs = [l for l in logs if l.get("category") == category]
return logs[-limit:][::-1]
except:
return []
def clear_logs(self, log_type: str = "all") -> bool:
try:
if log_type == "all":
for f in [self.log_file, self.event_log, self.error_log]:
if f.exists():
f.write_text("[]")
else:
file_map = {"events": self.event_log, "errors": self.error_log, "app": self.log_file}
if log_type in file_map and file_map[log_type].exists():
file_map[log_type].write_text("[]")
return True
except:
return False
def export_logs(self, log_type: str = "all") -> str:
logs = self.get_logs(log_type, limit=10000)
return json.dumps(logs, indent=2)
# Global logger instance
logger = LoggingService(LOGS_DIR)