diff --git a/src/mcp/server/mcpserver/utilities/logging.py b/src/mcp/server/mcpserver/utilities/logging.py index 04ca38853..6e94682f4 100644 --- a/src/mcp/server/mcpserver/utilities/logging.py +++ b/src/mcp/server/mcpserver/utilities/logging.py @@ -24,16 +24,18 @@ def configure_logging( Args: level: The log level to use. """ - handlers: list[logging.Handler] = [] - try: - from rich.console import Console - from rich.logging import RichHandler + logger = logging.getLogger("mcp") + if not logger.handlers: + try: + from rich.console import Console + from rich.logging import RichHandler - handlers.append(RichHandler(console=Console(stderr=True), rich_tracebacks=True)) - except ImportError: # pragma: no cover - pass + handler: logging.Handler = RichHandler(console=Console(stderr=True), rich_tracebacks=True) + except ImportError: # pragma: no cover + handler = logging.StreamHandler() - if not handlers: # pragma: no cover - handlers.append(logging.StreamHandler()) + handler.setFormatter(logging.Formatter("%(message)s")) + logger.addHandler(handler) - logging.basicConfig(level=level, format="%(message)s", handlers=handlers) + logger.setLevel(level) + logger.propagate = True diff --git a/tests/server/mcpserver/test_logging.py b/tests/server/mcpserver/test_logging.py new file mode 100644 index 000000000..0287711a1 --- /dev/null +++ b/tests/server/mcpserver/test_logging.py @@ -0,0 +1,54 @@ +import logging +from collections.abc import Iterator + +import pytest + +from mcp.server.mcpserver.utilities.logging import configure_logging + +LoggingState = tuple[logging.Logger, logging.Logger] + + +@pytest.fixture +def restore_logging_state() -> Iterator[LoggingState]: + root_logger = logging.getLogger() + mcp_logger = logging.getLogger("mcp") + mcp_handlers = list(mcp_logger.handlers) + root_level = root_logger.level + mcp_level = mcp_logger.level + mcp_propagate = mcp_logger.propagate + + mcp_logger.handlers.clear() + + try: + yield root_logger, mcp_logger + finally: + mcp_logger.handlers[:] = mcp_handlers + root_logger.setLevel(root_level) + mcp_logger.setLevel(mcp_level) + mcp_logger.propagate = mcp_propagate + + +def test_configure_logging_does_not_install_root_handler(restore_logging_state: LoggingState): + root_logger, mcp_logger = restore_logging_state + root_handlers = list(root_logger.handlers) + root_logger.setLevel(logging.WARNING) + + configure_logging("INFO") + + assert root_logger.handlers == root_handlers + assert root_logger.level == logging.WARNING + assert len(mcp_logger.handlers) == 1 + assert mcp_logger.level == logging.INFO + assert mcp_logger.propagate is True + + +def test_configure_logging_reuses_mcp_handler(restore_logging_state: LoggingState): + _, mcp_logger = restore_logging_state + + configure_logging("INFO") + handlers: list[logging.Handler] = list(mcp_logger.handlers) + configure_logging("DEBUG") + + assert mcp_logger.handlers == handlers + assert mcp_logger.level == logging.DEBUG + assert mcp_logger.propagate is True