107 lines
3.9 KiB
Python
107 lines
3.9 KiB
Python
"""
|
|
Simple logger with automatic function-level context tracking for KiCad MCP Server.
|
|
|
|
Usage examples:
|
|
# Creates logs in the "logs" directory by default
|
|
logger = Logger()
|
|
|
|
# To disable file logging completely
|
|
logger = Logger(log_dir=None)
|
|
|
|
# Or to specify a custom logs directory
|
|
logger = Logger(log_dir="custom_logs")
|
|
"""
|
|
import os
|
|
import sys
|
|
import logging
|
|
import inspect
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
class Logger:
|
|
"""
|
|
Simple logger that automatically tracks function-level context.
|
|
"""
|
|
def __init__(self, name=None, log_dir="logs", console_level=logging.INFO, file_level=logging.DEBUG):
|
|
"""
|
|
Initialize a logger with automatic function-level context.
|
|
|
|
Args:
|
|
name: Logger name (defaults to calling module name)
|
|
log_dir: Directory to store log files (default: "logs" directory)
|
|
Set to None to disable file logging
|
|
console_level: Logging level for console output
|
|
file_level: Logging level for file output
|
|
"""
|
|
# If no name provided, try to determine it from the calling module
|
|
if name is None:
|
|
frame = inspect.currentframe().f_back
|
|
module = inspect.getmodule(frame)
|
|
self.name = module.__name__ if module else "kicad_mcp"
|
|
else:
|
|
self.name = name
|
|
|
|
# Initialize Python's logger
|
|
self.logger = logging.getLogger(self.name)
|
|
self.logger.setLevel(logging.DEBUG) # Capture all levels, filtering at handler level
|
|
|
|
# Only configure if not already configured
|
|
if not self.logger.handlers and not logging.getLogger().handlers:
|
|
# Create formatter with detailed context
|
|
formatter = logging.Formatter(
|
|
'%(asctime)s [%(levelname)s] %(pathname)s:%(funcName)s:%(lineno)d - %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
# Set up console output
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_handler.setLevel(console_level)
|
|
console_handler.setFormatter(formatter)
|
|
self.logger.addHandler(console_handler)
|
|
|
|
# Set up file output by default unless explicitly disabled
|
|
if log_dir is not None:
|
|
log_dir_path = Path(log_dir)
|
|
log_dir_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
log_file = log_dir_path / f"kicad_mcp_{timestamp}.log"
|
|
|
|
file_handler = logging.FileHandler(log_file)
|
|
file_handler.setLevel(file_level)
|
|
file_handler.setFormatter(formatter)
|
|
self.logger.addHandler(file_handler)
|
|
|
|
self.info(f"Logging session started, log file: {log_file}")
|
|
|
|
def _get_caller_info(self):
|
|
"""Get information about the function that called the logger."""
|
|
# Skip this function, the log method, and get to the actual caller
|
|
frame = inspect.currentframe().f_back.f_back
|
|
return frame
|
|
|
|
def debug(self, message):
|
|
"""Log a debug message with caller context."""
|
|
self.logger.debug(message)
|
|
|
|
def info(self, message):
|
|
"""Log an info message with caller context."""
|
|
self.logger.info(message)
|
|
|
|
def warning(self, message):
|
|
"""Log a warning message with caller context."""
|
|
self.logger.warning(message)
|
|
|
|
def error(self, message):
|
|
"""Log an error message with caller context."""
|
|
self.logger.error(message)
|
|
|
|
def critical(self, message):
|
|
"""Log a critical message with caller context."""
|
|
self.logger.critical(message)
|
|
|
|
def exception(self, message):
|
|
"""Log an exception message with caller context and traceback."""
|
|
self.logger.exception(message)
|