Speech MCP
by Kvadratni
- src
- speech_mcp
- utils
"""
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,
}