Speech MCP

""" Centralized logging module for speech-mcp. This module provides a consistent logging interface for all components of the speech-mcp project, ensuring logs are written to a deterministic location and follow a consistent format. Usage: from speech_mcp.utils.logger import get_logger # Get a logger for a specific module logger = get_logger(__name__) # Log messages at different levels logger.debug("Debug message") logger.info("Info message") logger.warning("Warning message") logger.error("Error message") logger.critical("Critical message") # Log exceptions with traceback try: # Some code that might raise an exception raise ValueError("Example error") except Exception as e: logger.exception("An error occurred") """ import os import sys import logging import logging.handlers from pathlib import Path from typing import Dict, Optional # Define constants for log file paths LOG_DIR = os.path.expanduser("~/.speech-mcp/logs") MAIN_LOG_FILE = os.path.join(LOG_DIR, "speech-mcp.log") SERVER_LOG_FILE = os.path.join(LOG_DIR, "speech-mcp-server.log") UI_LOG_FILE = os.path.join(LOG_DIR, "speech-mcp-ui.log") TTS_LOG_FILE = os.path.join(LOG_DIR, "speech-mcp-tts.log") STT_LOG_FILE = os.path.join(LOG_DIR, "speech-mcp-stt.log") # Maximum log file size before rotation (10 MB) MAX_LOG_SIZE = 10 * 1024 * 1024 # Number of backup log files to keep BACKUP_COUNT = 5 # Default log level DEFAULT_LOG_LEVEL = logging.INFO # Map component names to log files COMPONENT_LOG_FILES = { "server": SERVER_LOG_FILE, "ui": UI_LOG_FILE, "tts": TTS_LOG_FILE, "stt": STT_LOG_FILE, } # Cache for loggers to avoid creating duplicates _loggers: Dict[str, logging.Logger] = {} def ensure_log_dir() -> None: """Ensure the log directory exists.""" os.makedirs(LOG_DIR, exist_ok=True) def get_logger(name: str, component: Optional[str] = None) -> logging.Logger: """ Get a logger with the specified name. Args: name: The name of the logger, typically __name__ of the calling module component: Optional component name to determine log file (server, ui, tts, stt) If None, it will be inferred from the name Returns: A configured logger instance """ # Check if we already created this logger if name in _loggers: return _loggers[name] # Create the logger logger = logging.getLogger(name) # Only configure the logger if it hasn't been configured yet if not logger.handlers: # Set the log level logger.setLevel(DEFAULT_LOG_LEVEL) # Ensure log directory exists ensure_log_dir() # Determine which log file to use based on component if component is None: # Try to infer component from name if "server" in name.lower(): log_file = SERVER_LOG_FILE elif "ui" in name.lower(): log_file = UI_LOG_FILE elif "tts" in name.lower(): log_file = TTS_LOG_FILE elif "stt" in name.lower() or "recognition" in name.lower(): log_file = STT_LOG_FILE else: log_file = MAIN_LOG_FILE else: # Use the specified component log_file = COMPONENT_LOG_FILES.get(component.lower(), MAIN_LOG_FILE) # Create a rotating file handler file_handler = logging.handlers.RotatingFileHandler( log_file, maxBytes=MAX_LOG_SIZE, backupCount=BACKUP_COUNT ) # Create a console handler console_handler = logging.StreamHandler(sys.stdout) # Create a formatter formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # Set the formatter for both handlers file_handler.setFormatter(formatter) console_handler.setFormatter(formatter) # Add the handlers to the logger logger.addHandler(file_handler) logger.addHandler(console_handler) # Prevent propagation to the root logger logger.propagate = False # Cache the logger _loggers[name] = logger return logger def set_log_level(level: int) -> None: """ Set the log level for all loggers. Args: level: The log level to set (e.g., logging.DEBUG, logging.INFO) """ for logger in _loggers.values(): logger.setLevel(level) def get_log_files() -> Dict[str, str]: """ Get a dictionary of log file paths. Returns: A dictionary mapping component names to log file paths """ return { "main": MAIN_LOG_FILE, "server": SERVER_LOG_FILE, "ui": UI_LOG_FILE, "tts": TTS_LOG_FILE, "stt": STT_LOG_FILE, }