Skip to main content
Glama
logging_config.py5.01 kB
"""Centralized logging configuration for Linux MCP Server. Simplified logging setup with standard Python logging infrastructure. Supports structured logging with extra fields for audit and diagnostic purposes. """ import json import logging import logging.handlers import os from pathlib import Path def get_log_directory() -> Path: """Get the log directory path, creating it if necessary.""" env_log_dir = os.getenv("LINUX_MCP_LOG_DIR") log_dir = Path(env_log_dir) if env_log_dir else Path.home() / ".local" / "share" / "linux-mcp-server" / "logs" log_dir.mkdir(parents=True, exist_ok=True) return log_dir def get_log_level() -> int: """Get the log level from environment variable (defaults to INFO).""" level_name = os.getenv("LINUX_MCP_LOG_LEVEL", "INFO").upper() return getattr(logging, level_name, logging.INFO) def get_retention_days() -> int: """Get the log retention days from environment variable (defaults to 10).""" try: return int(os.getenv("LINUX_MCP_LOG_RETENTION_DAYS", "10")) except ValueError: return 10 class StructuredFormatter(logging.Formatter): """ Structured log formatter supporting extra fields. Format: TIMESTAMP | LEVEL | MODULE | MESSAGE | key=value ... Extra fields added to LogRecord are appended as key=value pairs. """ STANDARD_FIELDS = { 'name', 'msg', 'args', 'created', 'filename', 'funcName', 'levelname', 'levelno', 'lineno', 'module', 'msecs', 'message', 'pathname', 'process', 'processName', 'relativeCreated', 'thread', 'threadName', 'exc_info', 'exc_text', 'stack_info', 'asctime', 'taskName' } def format(self, record: logging.LogRecord) -> str: """Format a log record with extra fields.""" # Base message base_msg = super().format(record) # Append extra fields as key=value pairs extra_fields = [f"{k}={v}" for k, v in record.__dict__.items() if k not in self.STANDARD_FIELDS] return f"{base_msg} | {' | '.join(extra_fields)}" if extra_fields else base_msg class JSONFormatter(logging.Formatter): """JSON log formatter for machine-readable logs.""" EXCLUDE_FIELDS = { 'args', 'exc_text', 'exc_info', 'stack_info', 'filename', 'funcName', 'lineno', 'module', 'msecs', 'pathname', 'process', 'processName', 'relativeCreated', 'thread', 'threadName', 'taskName' } def format(self, record: logging.LogRecord) -> str: """Format a log record as JSON.""" log_data = { 'timestamp': self.formatTime(record, self.datefmt), 'level': record.levelname, 'logger': record.name, 'message': record.getMessage(), } # Add exception info if present if record.exc_info: log_data['exception'] = self.formatException(record.exc_info) # Add extra fields for key, value in record.__dict__.items(): if (key not in self.EXCLUDE_FIELDS and key not in log_data and key not in {'name', 'msg', 'levelname', 'levelno', 'created'}): log_data[key] = value return json.dumps(log_data) def setup_logging(): """Set up logging with structured formatters and rotation.""" log_dir = get_log_directory() log_level = get_log_level() retention_days = get_retention_days() # Configure root logger root_logger = logging.getLogger() root_logger.setLevel(log_level) root_logger.handlers.clear() # Remove existing handlers # Human-readable text log text_handler = logging.handlers.TimedRotatingFileHandler( filename=log_dir / "server.log", when='midnight', interval=1, backupCount=retention_days, encoding='utf-8', ) text_handler.setLevel(log_level) text_handler.setFormatter(StructuredFormatter( '%(asctime)s | %(levelname)s | %(name)s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S' )) text_handler.suffix = "%Y-%m-%d" root_logger.addHandler(text_handler) # JSON log json_handler = logging.handlers.TimedRotatingFileHandler( filename=log_dir / "server.json", when='midnight', interval=1, backupCount=retention_days, encoding='utf-8', ) json_handler.setLevel(log_level) json_handler.setFormatter(JSONFormatter(datefmt='%Y-%m-%dT%H:%M:%S')) json_handler.suffix = "%Y-%m-%d" root_logger.addHandler(json_handler) # Console handler for development console_handler = logging.StreamHandler() console_handler.setLevel(log_level) console_handler.setFormatter(StructuredFormatter( '%(asctime)s | %(levelname)s | %(name)s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S' )) root_logger.addHandler(console_handler) logging.getLogger(__name__).info(f"Logging initialized: {log_dir}")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/narmaku/linux-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server