Skip to main content
Glama
logging_config.py5.18 kB
"""Logging configuration for Docker MCP server with dual output (console + files).""" import logging import os import sys from logging.handlers import RotatingFileHandler from pathlib import Path from typing import Any import structlog def setup_logging( log_dir: Path | str = Path("logs"), log_level: str | None = None, max_file_size_mb: int = 10, ) -> None: """Setup dual logging system: console + files with automatic truncation. Creates two log files: - mcp_server.log: General server operations - middleware.log: Middleware request/response tracking Args: log_dir: Directory for log files log_level: Log level (defaults to LOG_LEVEL env var or INFO) max_file_size_mb: Max file size before truncation (no backup files kept) """ log_dir = Path(log_dir) log_dir.mkdir(parents=True, exist_ok=True) # Get log level from environment or parameter if log_level is None: log_level = os.getenv("LOG_LEVEL", "INFO") log_level_num = getattr(logging, log_level.upper(), logging.INFO) max_bytes = max_file_size_mb * 1024 * 1024 # Clear any existing handlers to prevent duplicates logging.getLogger().handlers.clear() # Create formatters console_formatter = logging.Formatter( fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) # Server log handler (mcp_server.log) server_file_handler = RotatingFileHandler( log_dir / "mcp_server.log", maxBytes=max_bytes, backupCount=0, # Don't keep old files, just truncate encoding="utf-8", ) server_file_handler.setLevel(log_level_num) server_file_handler.setFormatter(console_formatter) # Middleware log handler (middleware.log) middleware_file_handler = RotatingFileHandler( log_dir / "middleware.log", maxBytes=max_bytes, backupCount=0, # Don't keep old files, just truncate encoding="utf-8", ) middleware_file_handler.setLevel(log_level_num) middleware_file_handler.setFormatter(console_formatter) # Console handler (for both server and middleware) console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(log_level_num) console_handler.setFormatter(console_formatter) # Configure root logger root_logger = logging.getLogger() root_logger.setLevel(log_level_num) root_logger.addHandler(console_handler) # Configure server logger (writes to mcp_server.log + console) server_logger = logging.getLogger("server") server_logger.addHandler(server_file_handler) server_logger.propagate = True # Also send to console via root logger # Configure middleware logger (writes to middleware.log + console) middleware_logger = logging.getLogger("middleware") middleware_logger.addHandler(middleware_file_handler) middleware_logger.propagate = True # Also send to console via root logger # Configure structlog to use standard library logging from structlog.stdlib import BoundLogger, LoggerFactory, ProcessorFormatter # Check if we're in a TTY or if we want colors in Docker force_colors = os.getenv("FORCE_COLOR", "").lower() in ("1", "true", "yes") is_tty = sys.stdout.isatty() # Use colored console renderer if TTY or force colors is enabled if is_tty or force_colors: renderer = structlog.dev.ConsoleRenderer(colors=True) else: renderer = structlog.processors.JSONRenderer() # Integrate with stdlib handlers so file and console both receive events structlog.configure( processors=[ structlog.contextvars.merge_contextvars, structlog.processors.add_log_level, structlog.processors.StackInfoRenderer(), structlog.processors.TimeStamper(fmt="iso"), structlog.stdlib.ProcessorFormatter.wrap_for_formatter, ], logger_factory=LoggerFactory(), wrapper_class=BoundLogger, cache_logger_on_first_use=True, ) # Apply structlog formatting via ProcessorFormatter on each handler console_handler.setFormatter(ProcessorFormatter(processor=renderer)) server_file_handler.setFormatter( ProcessorFormatter(processor=structlog.processors.JSONRenderer()) ) middleware_file_handler.setFormatter( ProcessorFormatter(processor=structlog.processors.JSONRenderer()) ) # Log initialization logger = structlog.get_logger("server") logger.info( "Logging system initialized", log_dir=str(log_dir.absolute()), log_level=log_level, max_file_size_mb=max_file_size_mb, server_log=str(log_dir / "mcp_server.log"), middleware_log=str(log_dir / "middleware.log"), ) def get_server_logger() -> Any: """Get logger for general server operations (writes to mcp_server.log).""" return structlog.get_logger("server") def get_middleware_logger() -> Any: """Get logger for middleware operations (writes to middleware.log).""" return structlog.get_logger("middleware") # Removed unused ensure_log_directory helper; server initializes log dir directly.

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/jmagar/docker-mcp'

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