import logging import json import sys from datetime import datetime from typing import Optional class StructuredFormatter(logging.Formatter): """JSON structured log formatter for production.""" def format(self, record): log_entry = { "timestamp": datetime.utcnow().isoformat() + "Z", "level": record.levelname, "logger": record.name, "message": record.getMessage(), } if record.exc_info and record.exc_info[0]: log_entry["exception"] = self.formatException(record.exc_info) if hasattr(record, "extra_data"): log_entry["data"] = record.extra_data return json.dumps(log_entry, ensure_ascii=False) class HumanFormatter(logging.Formatter): """Human-readable log formatter for development.""" def format(self, record): return f"{datetime.now().strftime('%H:%M:%S')} [{record.levelname}] {record.name}: {record.getMessage()}" def setup_logging(level: str = "INFO", structured: bool = False): """ Setup logging cho toàn bộ ứng dụng. Args: level: Log level (DEBUG, INFO, WARNING, ERROR) structured: True = JSON format (production), False = human readable (development) """ root_logger = logging.getLogger() root_logger.setLevel(getattr(logging, level.upper(), logging.INFO)) # Xóa existing handlers root_logger.handlers.clear() handler = logging.StreamHandler(sys.stdout) handler.setFormatter(StructuredFormatter() if structured else HumanFormatter()) root_logger.addHandler(handler) def log_event(logger: logging.Logger, level: str, message: str, **kwargs): """ Ghi log với structured data. Usage: log_event(logger, "info", "File processed", file_name="test.pdf", pages=5) """ extra = {"extra_data": kwargs} if kwargs else {} getattr(logger, level.lower(), logger.info)(message, extra=extra)